From 8c6749742f0fe5a0928e46abf2908fc86d1c409b Mon Sep 17 00:00:00 2001 From: QuickApps Date: Sat, 15 Oct 2011 14:35:43 +0200 Subject: [PATCH 0001/1024] new repo started --- .htaccess | 5 + LICENSE | 340 + Modules/empty | 0 README.mdown | 33 + app/.htaccess | 5 + app/Cake/Cache/Cache.php | 600 ++ app/Cake/Cache/Engine/ApcEngine.php | 130 + app/Cake/Cache/Engine/FileEngine.php | 311 + app/Cake/Cache/Engine/MemcacheEngine.php | 234 + app/Cake/Cache/Engine/WincacheEngine.php | 137 + app/Cake/Cache/Engine/XcacheEngine.php | 175 + app/Cake/Config/config.php | 19 + app/Cake/Config/routes.php | 86 + .../Config/unicode/casefolding/0080_00ff.php | 73 + .../Config/unicode/casefolding/0100_017f.php | 106 + .../Config/unicode/casefolding/0180_024F.php | 148 + .../Config/unicode/casefolding/0250_02af.php | 41 + .../Config/unicode/casefolding/0370_03ff.php | 102 + .../Config/unicode/casefolding/0400_04ff.php | 164 + .../Config/unicode/casefolding/0500_052f.php | 50 + .../Config/unicode/casefolding/0530_058f.php | 78 + .../Config/unicode/casefolding/1e00_1eff.php | 168 + .../Config/unicode/casefolding/1f00_1fff.php | 216 + .../Config/unicode/casefolding/2100_214f.php | 44 + .../Config/unicode/casefolding/2150_218f.php | 57 + .../Config/unicode/casefolding/2460_24ff.php | 66 + .../Config/unicode/casefolding/2c00_2c5f.php | 87 + .../Config/unicode/casefolding/2c60_2c7f.php | 48 + .../Config/unicode/casefolding/2c80_2cff.php | 90 + .../Config/unicode/casefolding/ff00_ffef.php | 66 + app/Cake/Configure/IniReader.php | 137 + app/Cake/Configure/PhpReader.php | 89 + app/Cake/Controller/AppController.php | 36 + app/Cake/Controller/CakeErrorController.php | 63 + app/Cake/Controller/Component.php | 157 + .../Controller/Component/AclComponent.php | 675 ++ .../Component/Auth/ActionsAuthorize.php | 41 + .../Component/Auth/BaseAuthenticate.php | 123 + .../Component/Auth/BaseAuthorize.php | 139 + .../Component/Auth/BasicAuthenticate.php | 122 + .../Component/Auth/ControllerAuthorize.php | 67 + .../Component/Auth/CrudAuthorize.php | 100 + .../Component/Auth/DigestAuthenticate.php | 262 + .../Component/Auth/FormAuthenticate.php | 68 + .../Controller/Component/AuthComponent.php | 702 ++ .../Controller/Component/CookieComponent.php | 495 ++ .../Controller/Component/EmailComponent.php | 489 ++ .../Component/PaginatorComponent.php | 378 ++ .../Component/RequestHandlerComponent.php | 674 ++ .../Component/SecurityComponent.php | 569 ++ .../Controller/Component/SessionComponent.php | 187 + app/Cake/Controller/ComponentCollection.php | 106 + app/Cake/Controller/Controller.php | 1169 ++++ app/Cake/Controller/PagesController.php | 82 + app/Cake/Controller/Scaffold.php | 443 ++ app/Cake/Core/App.php | 862 +++ app/Cake/Core/CakePlugin.php | 225 + app/Cake/Core/Configure.php | 380 ++ app/Cake/Core/Object.php | 197 + app/Cake/Error/ErrorHandler.php | 226 + app/Cake/Error/ExceptionRenderer.php | 284 + app/Cake/Error/exceptions.php | 491 ++ app/Cake/I18n/I18n.php | 570 ++ app/Cake/I18n/L10n.php | 472 ++ app/Cake/I18n/Multibyte.php | 1106 ++++ app/Cake/LICENSE.txt | 22 + app/Cake/Log/CakeLog.php | 208 + app/Cake/Log/CakeLogInterface.php | 35 + app/Cake/Log/Engine/FileLog.php | 71 + app/Cake/Model/AclNode.php | 184 + app/Cake/Model/Aco.php | 46 + app/Cake/Model/AcoAction.php | 46 + app/Cake/Model/AppModel.php | 37 + app/Cake/Model/Aro.php | 44 + app/Cake/Model/Behavior/AclBehavior.php | 137 + .../Model/Behavior/ContainableBehavior.php | 450 ++ app/Cake/Model/Behavior/TranslateBehavior.php | 530 ++ app/Cake/Model/Behavior/TreeBehavior.php | 964 +++ app/Cake/Model/BehaviorCollection.php | 275 + app/Cake/Model/CakeSchema.php | 697 ++ app/Cake/Model/ConnectionManager.php | 271 + app/Cake/Model/Datasource/CakeSession.php | 752 +++ app/Cake/Model/Datasource/DataSource.php | 423 ++ app/Cake/Model/Datasource/Database/Mysql.php | 680 ++ .../Model/Datasource/Database/Postgres.php | 891 +++ app/Cake/Model/Datasource/Database/Sqlite.php | 545 ++ .../Model/Datasource/Database/Sqlserver.php | 795 +++ app/Cake/Model/Datasource/DboSource.php | 3087 +++++++++ .../Model/Datasource/Session/CacheSession.php | 91 + .../Datasource/Session/DatabaseSession.php | 132 + app/Cake/Model/Model.php | 3429 ++++++++++ app/Cake/Model/ModelBehavior.php | 220 + app/Cake/Model/Permission.php | 79 + app/Cake/Network/CakeRequest.php | 810 +++ app/Cake/Network/CakeResponse.php | 665 ++ app/Cake/Network/CakeSocket.php | 278 + app/Cake/Network/Email/AbstractTransport.php | 76 + app/Cake/Network/Email/CakeEmail.php | 1340 ++++ app/Cake/Network/Email/DebugTransport.php | 41 + app/Cake/Network/Email/MailTransport.php | 54 + app/Cake/Network/Email/SmtpTransport.php | 229 + app/Cake/Network/Http/BasicAuthentication.php | 66 + .../Network/Http/DigestAuthentication.php | 104 + app/Cake/Network/Http/HttpResponse.php | 438 ++ app/Cake/Network/Http/HttpSocket.php | 947 +++ app/Cake/Routing/Dispatcher.php | 324 + app/Cake/Routing/Route/CakeRoute.php | 506 ++ app/Cake/Routing/Route/PluginShortRoute.php | 55 + app/Cake/Routing/Route/RedirectRoute.php | 93 + app/Cake/Routing/Router.php | 1062 +++ app/Cake/Utility/ClassRegistry.php | 341 + app/Cake/Utility/Debugger.php | 720 +++ app/Cake/Utility/File.php | 506 ++ app/Cake/Utility/Folder.php | 755 +++ app/Cake/Utility/Inflector.php | 553 ++ app/Cake/Utility/ObjectCollection.php | 257 + app/Cake/Utility/Sanitize.php | 256 + app/Cake/Utility/Security.php | 157 + app/Cake/Utility/Set.php | 1092 ++++ app/Cake/Utility/String.php | 353 + app/Cake/Utility/Validation.php | 883 +++ app/Cake/Utility/Xml.php | 334 + app/Cake/VERSION.txt | 20 + .../View/Elements/exception_stack_trace.ctp | 23 + app/Cake/View/Elements/sql_dump.ctp | 61 + app/Cake/View/Emails/html/default.ctp | 25 + app/Cake/View/Emails/text/default.ctp | 19 + app/Cake/View/Errors/error400.ctp | 31 + app/Cake/View/Errors/error500.ctp | 28 + app/Cake/View/Errors/missing_action.ctp | 42 + .../View/Errors/missing_behavior_class.ctp | 39 + .../View/Errors/missing_behavior_file.ctp | 39 + .../View/Errors/missing_component_class.ctp | 39 + .../View/Errors/missing_component_file.ctp | 39 + app/Cake/View/Errors/missing_connection.ctp | 33 + app/Cake/View/Errors/missing_controller.ctp | 39 + app/Cake/View/Errors/missing_database.ctp | 33 + .../View/Errors/missing_datasource_config.ctp | 29 + .../View/Errors/missing_datasource_file.ctp | 29 + app/Cake/View/Errors/missing_helper_class.ctp | 39 + app/Cake/View/Errors/missing_helper_file.ctp | 39 + app/Cake/View/Errors/missing_layout.ctp | 33 + app/Cake/View/Errors/missing_plugin.ctp | 45 + app/Cake/View/Errors/missing_table.ctp | 29 + app/Cake/View/Errors/missing_view.ctp | 33 + app/Cake/View/Errors/pdo_error.ctp | 38 + app/Cake/View/Errors/private_action.ctp | 29 + app/Cake/View/Errors/scaffold_error.ctp | 36 + app/Cake/View/Helper.php | 810 +++ app/Cake/View/Helper/AppHelper.php | 34 + app/Cake/View/Helper/CacheHelper.php | 301 + app/Cake/View/Helper/FormHelper.php | 2500 ++++++++ app/Cake/View/Helper/HtmlHelper.php | 1111 ++++ app/Cake/View/Helper/JqueryEngineHelper.php | 348 + app/Cake/View/Helper/JsBaseEngineHelper.php | 601 ++ app/Cake/View/Helper/JsHelper.php | 422 ++ app/Cake/View/Helper/MootoolsEngineHelper.php | 373 ++ app/Cake/View/Helper/NumberHelper.php | 255 + app/Cake/View/Helper/PaginatorHelper.php | 877 +++ .../View/Helper/PrototypeEngineHelper.php | 360 ++ app/Cake/View/Helper/RssHelper.php | 338 + app/Cake/View/Helper/SessionHelper.php | 148 + app/Cake/View/Helper/TextHelper.php | 371 ++ app/Cake/View/Helper/TimeHelper.php | 737 +++ app/Cake/View/HelperCollection.php | 94 + app/Cake/View/Layouts/Emails/html/default.ctp | 29 + app/Cake/View/Layouts/Emails/text/default.ctp | 21 + app/Cake/View/Layouts/ajax.ctp | 19 + app/Cake/View/Layouts/default.ctp | 60 + app/Cake/View/Layouts/flash.ctp | 37 + app/Cake/View/Layouts/js/default.ctp | 2 + app/Cake/View/Layouts/rss/default.ctp | 14 + app/Cake/View/Layouts/xml/default.ctp | 2 + app/Cake/View/MediaView.php | 248 + app/Cake/View/Pages/home.ctp | 173 + app/Cake/View/ScaffoldView.php | 89 + app/Cake/View/Scaffolds/form.ctp | 51 + app/Cake/View/Scaffolds/index.ctp | 94 + app/Cake/View/Scaffolds/view.ctp | 146 + app/Cake/View/ThemeView.php | 71 + app/Cake/View/View.php | 764 +++ app/Cake/basics.php | 732 +++ app/Cake/bootstrap.php | 154 + app/Config/Schema/manual/README.mdown | 7 + app/Config/Schema/manual/install | 0 .../Schema/manual/quickapps.install.sql | 802 +++ app/Config/Schema/tables/acos.sql | 56 + app/Config/Schema/tables/aros.sql | 56 + app/Config/Schema/tables/aros_acos.sql | 56 + app/Config/Schema/tables/block_custom.sql | 53 + app/Config/Schema/tables/block_regions.sql | 54 + app/Config/Schema/tables/block_roles.sql | 51 + app/Config/Schema/tables/blocks.sql | 62 + app/Config/Schema/tables/comments.sql | 60 + app/Config/Schema/tables/field_data.sql | 55 + app/Config/Schema/tables/fields.sql | 58 + app/Config/Schema/tables/i18n.sql | 58 + app/Config/Schema/tables/languages.sql | 58 + app/Config/Schema/tables/menu_links.sql | 67 + app/Config/Schema/tables/menus.sql | 53 + app/Config/Schema/tables/modules.sql | 53 + app/Config/Schema/tables/node_types.sql | 67 + app/Config/Schema/tables/nodes.sql | 69 + app/Config/Schema/tables/nodes_roles.sql | 51 + app/Config/Schema/tables/nodes_terms.sql | 52 + app/Config/Schema/tables/roles.sql | 52 + app/Config/Schema/tables/terms.sql | 59 + app/Config/Schema/tables/translations.sql | 54 + app/Config/Schema/tables/users.sql | 57 + app/Config/Schema/tables/users_roles.sql | 52 + app/Config/Schema/tables/variables.sql | 51 + app/Config/Schema/tables/vocabularies.sql | 57 + app/Config/bootstrap.php | 193 + app/Config/core.php | 327 + app/Config/database.php.install | 71 + app/Config/email.php.default | 88 + app/Config/routes.php | 53 + app/Controller/AppController.php | 294 + app/Controller/Component/HookComponent.php | 332 + .../Component/InstallerComponent.php | 881 +++ .../Component/QuickappsComponent.php | 562 ++ app/Controller/InstallController.php | 297 + app/Lib/Model/Model.php | 3429 ++++++++++ app/Locale/spa/LC_MESSAGES/core.po | 64 + app/Model/AppModel.php | 205 + app/Model/Behavior/SerializedBehavior.php | 103 + app/Model/Behavior/SluggableBehavior.php | 195 + app/Model/Behavior/WhoDidItBehavior.php | 99 + app/Model/Datasource/empty | 0 app/Plugin/block/Config/bootstrap.php | 0 app/Plugin/block/Config/routes.php | 0 .../block/Controller/BlockAppController.php | 14 + .../block/Controller/BlockController.php | 19 + .../Component/BlockHookComponent.php | 32 + .../block/Controller/ManageController.php | 214 + app/Plugin/block/Lib/empty | 0 .../block/Locale/spa/LC_MESSAGES/core.po | 136 + .../Model/Behavior/BlockHookBehavior.php | 14 + app/Plugin/block/Model/Block.php | 66 + app/Plugin/block/Model/BlockAppModel.php | 14 + app/Plugin/block/Model/BlockCustom.php | 20 + app/Plugin/block/Model/BlockRegion.php | 68 + app/Plugin/block/Model/BlockRole.php | 16 + app/Plugin/block/View/Elements/help.ctp | 18 + app/Plugin/block/View/Elements/toolbar.ctp | 6 + .../block/View/Helper/BlockHookHelper.php | 65 + app/Plugin/block/View/Manage/admin_add.ctp | 75 + app/Plugin/block/View/Manage/admin_edit.ctp | 98 + app/Plugin/block/View/Manage/admin_index.ctp | 239 + app/Plugin/block/block.yaml | 5 + app/Plugin/block/webroot/css/empty | 0 app/Plugin/block/webroot/img/empty | 0 app/Plugin/block/webroot/js/empty | 0 app/Plugin/comment/Config/bootstrap.php | 0 app/Plugin/comment/Config/routes.php | 0 .../Controller/CommentAppController.php | 28 + .../comment/Controller/CommentController.php | 19 + app/Plugin/comment/Controller/Component/empty | 0 .../Controller/PublishedController.php | 57 + .../Controller/UnpublishedController.php | 56 + app/Plugin/comment/Lib/Nbbc.php | 2049 ++++++ app/Plugin/comment/Lib/nbbc/CHANGELOG | 185 + app/Plugin/comment/Lib/nbbc/LICENSE | 28 + app/Plugin/comment/Lib/nbbc/nbbc.php | 2049 ++++++ .../comment/Lib/nbbc/powered_by_nbbc.png | Bin 0 -> 1821 bytes .../Lib/nbbc/powered_by_nbbc_small.png | Bin 0 -> 1136 bytes app/Plugin/comment/Lib/nbbc/smileys/angry.gif | Bin 0 -> 238 bytes app/Plugin/comment/Lib/nbbc/smileys/anime.gif | Bin 0 -> 240 bytes .../comment/Lib/nbbc/smileys/bigeyes.gif | Bin 0 -> 234 bytes .../comment/Lib/nbbc/smileys/bigsmile.gif | Bin 0 -> 227 bytes .../comment/Lib/nbbc/smileys/bigwink.gif | Bin 0 -> 224 bytes app/Plugin/comment/Lib/nbbc/smileys/blue.gif | Bin 0 -> 233 bytes .../comment/Lib/nbbc/smileys/boggle.gif | Bin 0 -> 227 bytes .../comment/Lib/nbbc/smileys/confuse.gif | Bin 0 -> 230 bytes app/Plugin/comment/Lib/nbbc/smileys/cool.gif | Bin 0 -> 217 bytes app/Plugin/comment/Lib/nbbc/smileys/evil.gif | Bin 0 -> 236 bytes app/Plugin/comment/Lib/nbbc/smileys/frown.gif | Bin 0 -> 232 bytes app/Plugin/comment/Lib/nbbc/smileys/heart.gif | Bin 0 -> 167 bytes .../comment/Lib/nbbc/smileys/irritated.gif | Bin 0 -> 232 bytes app/Plugin/comment/Lib/nbbc/smileys/laugh.gif | Bin 0 -> 234 bytes .../comment/Lib/nbbc/smileys/lookleft.gif | Bin 0 -> 225 bytes .../comment/Lib/nbbc/smileys/lookright.gif | Bin 0 -> 227 bytes .../comment/Lib/nbbc/smileys/neutral.gif | Bin 0 -> 225 bytes app/Plugin/comment/Lib/nbbc/smileys/saint.gif | Bin 0 -> 240 bytes .../comment/Lib/nbbc/smileys/sleepy.gif | Bin 0 -> 217 bytes app/Plugin/comment/Lib/nbbc/smileys/smile.gif | Bin 0 -> 233 bytes .../comment/Lib/nbbc/smileys/smile3.gif | Bin 0 -> 238 bytes .../comment/Lib/nbbc/smileys/sneaky.gif | Bin 0 -> 231 bytes app/Plugin/comment/Lib/nbbc/smileys/star.gif | Bin 0 -> 208 bytes .../comment/Lib/nbbc/smileys/surprise.gif | Bin 0 -> 225 bytes .../comment/Lib/nbbc/smileys/sweatdrop.gif | Bin 0 -> 236 bytes app/Plugin/comment/Lib/nbbc/smileys/teeth.gif | Bin 0 -> 208 bytes .../comment/Lib/nbbc/smileys/tongue.gif | Bin 0 -> 230 bytes app/Plugin/comment/Lib/nbbc/smileys/wink.gif | Bin 0 -> 229 bytes app/Plugin/comment/Lib/nbbc/smileys/wink3.gif | Bin 0 -> 234 bytes app/Plugin/comment/Lib/nbbc/smileys/worry.gif | Bin 0 -> 230 bytes .../comment/Lib/nbbc/src/nbbc_email.php | 158 + app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php | 591 ++ app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php | 655 ++ app/Plugin/comment/Lib/nbbc/src/nbbc_main.php | 149 + .../comment/Lib/nbbc/src/nbbc_parse.php | 2146 +++++++ app/Plugin/comment/Lib/nbbc/test_nbbc.php | 1214 ++++ app/Plugin/comment/Lib/nbbc/tools/Makefile | 16 + .../Lib/nbbc/tools/collect_smileys.php | 106 + app/Plugin/comment/Lib/nbbc/tools/merge.pl | 115 + .../comment/Locale/spa/LC_MESSAGES/core.po | 86 + .../comment/Model/Behavior/BBCodeBehavior.php | 109 + app/Plugin/comment/Model/Comment.php | 172 + app/Plugin/comment/Model/CommentAppModel.php | 14 + app/Plugin/comment/View/Elements/help.ctp | 22 + app/Plugin/comment/View/Elements/toolbar.ctp | 6 + .../comment/View/Helper/CommentHookHelper.php | 50 + .../comment/View/Published/admin_index.ctp | 59 + .../comment/View/Unpublished/admin_index.ctp | 59 + app/Plugin/comment/comment.yaml | 5 + app/Plugin/comment/webroot/css/empty | 0 app/Plugin/comment/webroot/img/empty | 0 .../comment/webroot/js/jquery.scrollTo-min.js | 11 + .../webroot/js/markItUp/jquery.markitup.js | 594 ++ .../comment/webroot/js/markItUp/locale/eng.js | 0 .../comment/webroot/js/markItUp/locale/spa.js | 18 + .../js/markItUp/sets/bbcode/images/bold.png | Bin 0 -> 304 bytes .../js/markItUp/sets/bbcode/images/clean.png | Bin 0 -> 667 bytes .../js/markItUp/sets/bbcode/images/code.png | Bin 0 -> 859 bytes .../js/markItUp/sets/bbcode/images/fonts.png | Bin 0 -> 567 bytes .../js/markItUp/sets/bbcode/images/italic.png | Bin 0 -> 223 bytes .../js/markItUp/sets/bbcode/images/link.png | Bin 0 -> 343 bytes .../sets/bbcode/images/list-bullet.png | Bin 0 -> 344 bytes .../markItUp/sets/bbcode/images/list-item.png | Bin 0 -> 248 bytes .../sets/bbcode/images/list-numeric.png | Bin 0 -> 357 bytes .../markItUp/sets/bbcode/images/picture.png | Bin 0 -> 606 bytes .../markItUp/sets/bbcode/images/preview.png | Bin 0 -> 537 bytes .../js/markItUp/sets/bbcode/images/quotes.png | Bin 0 -> 743 bytes .../js/markItUp/sets/bbcode/images/stroke.png | Bin 0 -> 269 bytes .../markItUp/sets/bbcode/images/underline.png | Bin 0 -> 273 bytes .../js/markItUp/sets/bbcode/images/video.png | Bin 0 -> 1341 bytes .../js/markItUp/sets/bbcode/readme.txt | 11 + .../webroot/js/markItUp/sets/bbcode/set.js | 48 + .../webroot/js/markItUp/sets/bbcode/style.css | 43 + .../js/markItUp/sets/default/images/bold.png | Bin 0 -> 304 bytes .../js/markItUp/sets/default/images/clean.png | Bin 0 -> 667 bytes .../js/markItUp/sets/default/images/image.png | Bin 0 -> 516 bytes .../markItUp/sets/default/images/italic.png | Bin 0 -> 223 bytes .../js/markItUp/sets/default/images/link.png | Bin 0 -> 343 bytes .../sets/default/images/list-bullet.png | Bin 0 -> 344 bytes .../sets/default/images/list-numeric.png | Bin 0 -> 357 bytes .../markItUp/sets/default/images/picture.png | Bin 0 -> 606 bytes .../markItUp/sets/default/images/preview.png | Bin 0 -> 537 bytes .../markItUp/sets/default/images/stroke.png | Bin 0 -> 269 bytes .../webroot/js/markItUp/sets/default/set.js | 45 + .../js/markItUp/sets/default/style.css | 98 + .../skins/markitup/images/bg-container.png | Bin 0 -> 322 bytes .../markitup/images/bg-editor-bbcode.png | Bin 0 -> 1642 bytes .../markitup/images/bg-editor-dotclear.png | Bin 0 -> 1682 bytes .../skins/markitup/images/bg-editor-html.png | Bin 0 -> 1534 bytes .../skins/markitup/images/bg-editor-json.png | Bin 0 -> 1529 bytes .../markitup/images/bg-editor-markdown.png | Bin 0 -> 1783 bytes .../markitup/images/bg-editor-textile.png | Bin 0 -> 1659 bytes .../skins/markitup/images/bg-editor-wiki.png | Bin 0 -> 1488 bytes .../skins/markitup/images/bg-editor-xml.png | Bin 0 -> 1495 bytes .../skins/markitup/images/bg-editor.png | Bin 0 -> 1745 bytes .../markItUp/skins/markitup/images/handle.png | Bin 0 -> 258 bytes .../markItUp/skins/markitup/images/menu.png | Bin 0 -> 254 bytes .../skins/markitup/images/submenu.png | Bin 0 -> 240 bytes .../js/markItUp/skins/markitup/style.css | 147 + .../markItUp/skins/simple/images/handle.png | Bin 0 -> 258 bytes .../js/markItUp/skins/simple/images/menu.png | Bin 0 -> 27151 bytes .../markItUp/skins/simple/images/submenu.png | Bin 0 -> 240 bytes .../js/markItUp/skins/simple/style.css | 118 + app/Plugin/field/Config/bootstrap.php | 0 app/Plugin/field/Config/routes.php | 0 .../Component/FieldHookComponent.php | 51 + .../field/Controller/FieldAppController.php | 14 + .../field/Controller/HandlerController.php | 27 + .../Fields/field_file/Config/bootstrap.php | 0 .../field/Fields/field_file/Config/routes.php | 0 .../field_file/Controller/Component/empty | 0 .../Controller/FieldFileAppController.php | 14 + .../Controller/UploadifyController.php | 73 + .../field/Fields/field_file/Lib/FieldFile.php | 213 + .../field_file/Locale/spa/LC_MESSAGES/core.po | 33 + .../Model/Behavior/FieldFileHookBehavior.php | 172 + .../field_file/Model/FieldFileAppModel.php | 4 + .../Fields/field_file/View/Elements/edit.ctp | 158 + .../View/Elements/formatter_from.ctp | 14 + .../View/Elements/settings_from.ctp | 40 + .../Fields/field_file/View/Elements/view.ctp | 28 + .../View/Helper/FieldFileHookHelper.php | 69 + .../field/Fields/field_file/field_file.yaml | 3 + .../field_file/webroot/css/field_file.css | 23 + .../img/icons/application-octet-stream.png | Bin 0 -> 189 bytes .../webroot/img/icons/application-pdf.png | Bin 0 -> 346 bytes .../img/icons/application-x-executable.png | Bin 0 -> 189 bytes .../webroot/img/icons/audio-x-generic.png | Bin 0 -> 314 bytes .../webroot/img/icons/image-x-generic.png | Bin 0 -> 385 bytes .../webroot/img/icons/package-x-generic.png | Bin 0 -> 260 bytes .../webroot/img/icons/text-html.png | Bin 0 -> 265 bytes .../webroot/img/icons/text-plain.png | Bin 0 -> 220 bytes .../webroot/img/icons/text-x-generic.png | Bin 0 -> 220 bytes .../webroot/img/icons/text-x-script.png | Bin 0 -> 276 bytes .../webroot/img/icons/video-x-generic.png | Bin 0 -> 214 bytes .../webroot/img/icons/x-office-document.png | Bin 0 -> 196 bytes .../img/icons/x-office-presentation.png | Bin 0 -> 181 bytes .../img/icons/x-office-spreadsheet.png | Bin 0 -> 183 bytes .../field_file/webroot/js/field_file.js | 206 + .../field_file/webroot/js/locale.eng.js | 0 .../field_file/webroot/js/locale.spa.js | 3 + .../webroot/js/uploadify/cancel.png | Bin 0 -> 2960 bytes .../field_file/webroot/js/uploadify/check.php | 35 + .../webroot/js/uploadify/expressInstall.swf | Bin 0 -> 727 bytes .../webroot/js/uploadify/jquery-1.4.2.min.js | 154 + .../js/uploadify/jquery.uploadify.v2.1.4.js | 296 + .../uploadify/jquery.uploadify.v2.1.4.min.js | 26 + .../webroot/js/uploadify/swfobject.js | 4 + .../js/uploadify/uploadify.allglyphs.swf | Bin 0 -> 36521 bytes .../webroot/js/uploadify/uploadify.css | 52 + .../webroot/js/uploadify/uploadify.swf | Bin 0 -> 22894 bytes .../Fields/field_list/Config/bootstrap.php | 0 .../field/Fields/field_list/Config/routes.php | 0 .../field_list/Controller/Component/empty | 0 .../Model/Behavior/FieldListHookBehavior.php | 87 + .../field_list/Model/FieldListAppModel.php | 4 + .../Fields/field_list/View/Elements/edit.ctp | 38 + .../View/Elements/formatter_from.ctp | 9 + .../View/Elements/settings_from.ctp | 23 + .../Fields/field_list/View/Elements/view.ctp | 27 + .../View/Helper/FieldListHookHelper.php | 43 + .../field/Fields/field_list/field_list.yaml | 3 + .../Fields/field_text/Config/bootstrap.php | 0 .../field/Fields/field_text/Config/routes.php | 0 .../field_text/Controller/Component/empty | 0 .../field/Fields/field_text/Lib/Html2text.php | 489 ++ .../field/Fields/field_text/Lib/Markdown.php | 2789 ++++++++ .../Model/Behavior/FieldTextHookBehavior.php | 159 + .../field_text/Model/FieldTextAppModel.php | 4 + .../Fields/field_text/View/Elements/edit.ctp | 46 + .../View/Elements/formatter_from.ctp | 30 + .../View/Elements/settings_from.ctp | 96 + .../Fields/field_text/View/Elements/view.ctp | 27 + .../View/Helper/FieldTextHookHelper.php | 127 + .../field/Fields/field_text/field_text.yaml | 3 + app/Plugin/field/Lib/empty | 0 .../field/Locale/spa/LC_MESSAGES/core.po | 16 + .../Model/Behavior/FieldHookBehavior.php | 46 + .../Model/Behavior/FieldableBehavior.php | 555 ++ app/Plugin/field/Model/Field.php | 212 + app/Plugin/field/Model/FieldAppModel.php | 14 + app/Plugin/field/Model/FieldData.php | 22 + app/Plugin/field/View/Elements/help.ctp | 28 + app/Plugin/field/View/Helper/empty | 0 app/Plugin/field/field.yaml | 5 + app/Plugin/field/webroot/css/empty | 0 app/Plugin/field/webroot/img/empty | 0 app/Plugin/field/webroot/js/empty | 0 app/Plugin/locale/Config/bootstrap.php | 8 + app/Plugin/locale/Config/routes.php | 0 app/Plugin/locale/Controller/Component/empty | 0 .../locale/Controller/LanguagesController.php | 147 + .../locale/Controller/LocaleAppController.php | 14 + .../locale/Controller/LocaleController.php | 19 + .../locale/Controller/PackagesController.php | 142 + .../Controller/TranslationsController.php | 82 + app/Plugin/locale/Lib/Locale.php | 140 + .../locale/Locale/spa/LC_MESSAGES/core.po | 180 + app/Plugin/locale/Model/Behavior/empty | 0 .../locale/Model/Internationalization.php | 16 + app/Plugin/locale/Model/Language.php | 102 + app/Plugin/locale/Model/LocaleAppModel.php | 14 + app/Plugin/locale/Model/Translation.php | 76 + app/Plugin/locale/View/Elements/help.ctp | 37 + .../Elements/locale_language_switcher.ctp | 19 + .../locale_language_switcher_settings.ctp | 2 + app/Plugin/locale/View/Elements/toolbar.ctp | 11 + .../locale/View/Helper/LocaleHookHelper.php | 41 + .../locale/View/Languages/admin_edit.ctp | 68 + .../locale/View/Languages/admin_index.ctp | 135 + .../locale/View/Packages/admin_index.ctp | 71 + .../locale/View/Translations/admin_add.ctp | 53 + .../locale/View/Translations/admin_edit.ctp | 46 + .../locale/View/Translations/admin_list.ctp | 32 + app/Plugin/locale/locale.yaml | 5 + app/Plugin/locale/webroot/css/empty | 0 app/Plugin/locale/webroot/img/default.png | Bin 0 -> 295 bytes app/Plugin/locale/webroot/img/flags/ad.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/ae.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/af.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/ag.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/ai.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/al.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/am.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/an.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/ao.gif | Bin 0 -> 244 bytes app/Plugin/locale/webroot/img/flags/ar.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/as.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/at.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/au.gif | Bin 0 -> 378 bytes app/Plugin/locale/webroot/img/flags/aw.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/ax.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/az.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/ba.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/bb.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/bd.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/be.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/bf.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/bg.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/bh.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/bi.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/bj.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/bm.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/bn.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/bo.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/br.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/bs.gif | Bin 0 -> 351 bytes app/Plugin/locale/webroot/img/flags/bt.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/bv.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/bw.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/by.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/bz.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/ca.gif | Bin 0 -> 376 bytes .../locale/webroot/img/flags/catalonia.gif | Bin 0 -> 238 bytes app/Plugin/locale/webroot/img/flags/cc.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/cd.gif | Bin 0 -> 243 bytes app/Plugin/locale/webroot/img/flags/cf.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/cg.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/ch.gif | Bin 0 -> 332 bytes app/Plugin/locale/webroot/img/flags/ci.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/ck.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/cl.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/cm.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/cn.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/co.gif | Bin 0 -> 353 bytes app/Plugin/locale/webroot/img/flags/cr.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/cs.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/cu.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/cv.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/cx.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/cy.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/cz.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/de.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/dj.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/dk.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/dm.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/do.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/dz.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/ec.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/ee.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/eg.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/eh.gif | Bin 0 -> 359 bytes .../locale/webroot/img/flags/england.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/er.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/es.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/et.gif | Bin 0 -> 364 bytes .../webroot/img/flags/europeanunion.gif | Bin 0 -> 171 bytes app/Plugin/locale/webroot/img/flags/eus.gif | Bin 0 -> 986 bytes app/Plugin/locale/webroot/img/flags/fam.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/fi.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/fj.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/fk.gif | Bin 0 -> 372 bytes app/Plugin/locale/webroot/img/flags/fm.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/fo.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/fr.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/ga.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/gb.gif | Bin 0 -> 260 bytes app/Plugin/locale/webroot/img/flags/gd.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/ge.gif | Bin 0 -> 379 bytes app/Plugin/locale/webroot/img/flags/gf.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/gh.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/gi.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/gl.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/gm.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/gn.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/gp.gif | Bin 0 -> 357 bytes app/Plugin/locale/webroot/img/flags/gq.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/gr.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/gs.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/gt.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/gu.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/gw.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/gy.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/hk.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/hm.gif | Bin 0 -> 378 bytes app/Plugin/locale/webroot/img/flags/hn.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/hr.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/ht.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/hu.gif | Bin 0 -> 357 bytes app/Plugin/locale/webroot/img/flags/id.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/ie.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/il.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/in.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/io.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/iq.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/ir.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/is.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/it.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/jm.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/jo.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/jp.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/ke.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/kg.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/kh.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/ki.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/km.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/kn.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/kp.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/kr.gif | Bin 0 -> 385 bytes app/Plugin/locale/webroot/img/flags/kw.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/ky.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/kz.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/la.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/lb.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/lc.gif | Bin 0 -> 259 bytes app/Plugin/locale/webroot/img/flags/li.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/lk.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/lr.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/ls.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/lt.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/lu.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/lv.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/ly.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/ma.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/mc.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/md.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/me.gif | Bin 0 -> 238 bytes app/Plugin/locale/webroot/img/flags/mg.gif | Bin 0 -> 372 bytes app/Plugin/locale/webroot/img/flags/mh.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/mk.gif | Bin 0 -> 382 bytes app/Plugin/locale/webroot/img/flags/ml.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/mm.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/mn.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/mo.gif | Bin 0 -> 378 bytes app/Plugin/locale/webroot/img/flags/mp.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/mq.gif | Bin 0 -> 379 bytes app/Plugin/locale/webroot/img/flags/mr.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/ms.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/mt.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/mu.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/mv.gif | Bin 0 -> 372 bytes app/Plugin/locale/webroot/img/flags/mw.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/mx.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/my.gif | Bin 0 -> 375 bytes app/Plugin/locale/webroot/img/flags/mz.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/na.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/nc.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/ne.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/nf.gif | Bin 0 -> 375 bytes app/Plugin/locale/webroot/img/flags/ng.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/ni.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/nl.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/no.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/np.gif | Bin 0 -> 302 bytes app/Plugin/locale/webroot/img/flags/nr.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/nu.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/nz.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/om.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/pa.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/pe.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/pf.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/pg.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/ph.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/pk.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/pl.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/pm.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/pn.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/pr.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/ps.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/pt.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/pw.gif | Bin 0 -> 374 bytes app/Plugin/locale/webroot/img/flags/py.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/qa.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/re.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/ro.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/rs.gif | Bin 0 -> 238 bytes app/Plugin/locale/webroot/img/flags/ru.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/rw.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/sa.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/sb.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/sc.gif | Bin 0 -> 357 bytes .../locale/webroot/img/flags/scotland.gif | Bin 0 -> 378 bytes app/Plugin/locale/webroot/img/flags/sd.gif | Bin 0 -> 355 bytes app/Plugin/locale/webroot/img/flags/se.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/sg.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/sh.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/si.gif | Bin 0 -> 362 bytes app/Plugin/locale/webroot/img/flags/sj.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/sk.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/sl.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/sm.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/sn.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/so.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/sr.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/st.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/sv.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/sy.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/sz.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/tc.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/td.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/tf.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/tg.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/th.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/tj.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/tk.gif | Bin 0 -> 372 bytes app/Plugin/locale/webroot/img/flags/tl.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/tm.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/tn.gif | Bin 0 -> 375 bytes app/Plugin/locale/webroot/img/flags/to.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/tr.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/tt.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/tv.gif | Bin 0 -> 361 bytes app/Plugin/locale/webroot/img/flags/tw.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/tz.gif | Bin 0 -> 366 bytes app/Plugin/locale/webroot/img/flags/ua.gif | Bin 0 -> 360 bytes app/Plugin/locale/webroot/img/flags/ug.gif | Bin 0 -> 359 bytes app/Plugin/locale/webroot/img/flags/um.gif | Bin 0 -> 371 bytes app/Plugin/locale/webroot/img/flags/us.gif | Bin 0 -> 367 bytes app/Plugin/locale/webroot/img/flags/uy.gif | Bin 0 -> 373 bytes app/Plugin/locale/webroot/img/flags/uz.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/va.gif | Bin 0 -> 369 bytes app/Plugin/locale/webroot/img/flags/vc.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/ve.gif | Bin 0 -> 364 bytes app/Plugin/locale/webroot/img/flags/vg.gif | Bin 0 -> 368 bytes app/Plugin/locale/webroot/img/flags/vi.gif | Bin 0 -> 376 bytes app/Plugin/locale/webroot/img/flags/vn.gif | Bin 0 -> 370 bytes app/Plugin/locale/webroot/img/flags/vs.gif | Bin 0 -> 2054 bytes app/Plugin/locale/webroot/img/flags/vu.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/wales.gif | Bin 0 -> 372 bytes app/Plugin/locale/webroot/img/flags/wf.gif | Bin 0 -> 377 bytes app/Plugin/locale/webroot/img/flags/ws.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/img/flags/ye.gif | Bin 0 -> 356 bytes app/Plugin/locale/webroot/img/flags/yt.gif | Bin 0 -> 382 bytes app/Plugin/locale/webroot/img/flags/za.gif | Bin 0 -> 363 bytes app/Plugin/locale/webroot/img/flags/zm.gif | Bin 0 -> 358 bytes app/Plugin/locale/webroot/img/flags/zw.gif | Bin 0 -> 365 bytes app/Plugin/locale/webroot/js/empty | 0 app/Plugin/menu/Config/bootstrap.php | 0 app/Plugin/menu/Config/routes.php | 0 app/Plugin/menu/Controller/Component/empty | 0 .../menu/Controller/ManageController.php | 244 + .../menu/Controller/MenuAppController.php | 14 + app/Plugin/menu/Controller/MenuController.php | 19 + app/Plugin/menu/Lib/empty | 0 .../menu/Locale/spa/LC_MESSAGES/core.po | 144 + app/Plugin/menu/Model/Behavior/empty | 0 app/Plugin/menu/Model/Menu.php | 101 + app/Plugin/menu/Model/MenuAppModel.php | 14 + app/Plugin/menu/Model/MenuLink.php | 79 + app/Plugin/menu/View/Elements/help.ctp | 23 + app/Plugin/menu/View/Elements/menu_edit.ctp | 78 + .../menu/View/Elements/menu_link_node.ctp | 8 + app/Plugin/menu/View/Elements/toolbar.ctp | 6 + .../menu/View/Helper/MenuHookHelper.php | 19 + app/Plugin/menu/View/Helper/TreeHelper.php | 457 ++ app/Plugin/menu/View/Manage/admin_add.ctp | 7 + .../menu/View/Manage/admin_add_link.ctp | 46 + app/Plugin/menu/View/Manage/admin_edit.ctp | 7 + .../menu/View/Manage/admin_edit_link.ctp | 56 + app/Plugin/menu/View/Manage/admin_index.ctp | 29 + app/Plugin/menu/View/Manage/admin_links.ctp | 6 + app/Plugin/menu/menu.yaml | 5 + app/Plugin/menu/webroot/css/empty | 0 app/Plugin/menu/webroot/img/empty | 0 .../js/nestedSortable/jquery-1.5.2.min.js | 16 + .../jquery-ui-1.8.11.custom.min.js | 110 + .../jquery.ui.nestedSortable.js | 356 ++ app/Plugin/node/Config/bootstrap.php | 7 + app/Plugin/node/Config/routes.php | 4 + .../Component/NodeHookComponent.php | 27 + .../node/Controller/ContentsController.php | 261 + .../node/Controller/NodeAppController.php | 14 + app/Plugin/node/Controller/NodeController.php | 478 ++ .../node/Controller/TypesController.php | 215 + app/Plugin/node/Lib/empty | 0 .../node/Locale/spa/LC_MESSAGES/core.po | 432 ++ .../node/Model/Behavior/NodeHookBehavior.php | 64 + app/Plugin/node/Model/Node.php | 133 + app/Plugin/node/Model/NodeAppModel.php | 14 + app/Plugin/node/Model/NodeType.php | 30 + app/Plugin/node/View/Contents/admin_add.ctp | 79 + .../node/View/Contents/admin_create.ctp | 24 + app/Plugin/node/View/Contents/admin_edit.ctp | 89 + app/Plugin/node/View/Contents/admin_index.ctp | 156 + app/Plugin/node/View/Elements/help.ctp | 30 + .../node/View/Elements/node/comments.ctp | 83 + .../node/View/Elements/node/comments_form.ctp | 69 + app/Plugin/node/View/Elements/node/edit.ctp | 14 + .../node/View/Elements/node/front_page.ctp | 18 + app/Plugin/node/View/Elements/node/node.ctp | 30 + .../node/View/Elements/search_block.ctp | 13 + app/Plugin/node/View/Elements/search_form.ctp | 31 + .../node/View/Elements/toolbar-display.ctp | 10 + .../node/View/Elements/toolbar-index.ctp | 7 + .../node/View/Elements/toolbar-types.ctp | 4 + .../node/View/Helper/NodeHookHelper.php | 78 + app/Plugin/node/View/Node/details.ctp | 33 + app/Plugin/node/View/Node/index.ctp | 1 + app/Plugin/node/View/Node/search.ctp | 26 + app/Plugin/node/View/Types/admin_add.ctp | 50 + app/Plugin/node/View/Types/admin_display.ctp | 50 + app/Plugin/node/View/Types/admin_edit.ctp | 51 + .../node/View/Types/admin_field_formatter.ctp | 17 + .../node/View/Types/admin_field_settings.ctp | 17 + app/Plugin/node/View/Types/admin_fields.ctp | 110 + app/Plugin/node/View/Types/admin_index.ctp | 25 + app/Plugin/node/node.yaml | 5 + app/Plugin/node/webroot/css/empty | 0 app/Plugin/node/webroot/img/promote.png | Bin 0 -> 897 bytes app/Plugin/node/webroot/img/sticky.png | Bin 0 -> 1063 bytes app/Plugin/node/webroot/js/empty | 0 app/Plugin/system/Config/bootstrap.php | 14 + app/Plugin/system/Config/routes.php | 0 .../Component/SystemHookComponent.php | 19 + .../Controller/ConfigurationController.php | 60 + .../system/Controller/DashboardController.php | 19 + .../system/Controller/HelpController.php | 33 + .../system/Controller/ModulesController.php | 123 + .../system/Controller/StructureController.php | 19 + .../system/Controller/SystemAppController.php | 14 + .../system/Controller/SystemController.php | 19 + .../system/Controller/ThemesController.php | 128 + app/Plugin/system/Lib/empty | 0 .../system/Locale/spa/LC_MESSAGES/core.po | 172 + app/Plugin/system/Model/Behavior/empty | 0 app/Plugin/system/Model/Module.php | 30 + app/Plugin/system/Model/SystemAppModel.php | 14 + app/Plugin/system/Model/Variable.php | 54 + .../system/View/Configuration/admin_index.ctp | 64 + .../system/View/Dashboard/admin_index.ctp | 7 + app/Plugin/system/View/Elements/help.ctp | 101 + .../View/Elements/system_recent_content.ctp | 14 + app/Plugin/system/View/Help/admin_index.ctp | 11 + app/Plugin/system/View/Help/admin_module.ctp | 1 + .../system/View/Helper/SystemHookHelper.php | 41 + .../system/View/Modules/admin_index.ctp | 76 + .../system/View/Modules/admin_settings.ctp | 1 + .../system/View/Structure/admin_index.ctp | 1 + app/Plugin/system/View/Themes/admin_index.ctp | 99 + .../system/View/Themes/admin_settings.ctp | 30 + app/Plugin/system/system.yaml | 5 + app/Plugin/system/webroot/css/empty | 0 app/Plugin/system/webroot/img/empty | 0 app/Plugin/system/webroot/js/empty | 0 app/Plugin/taxonomy/Config/bootstrap.php | 0 app/Plugin/taxonomy/Config/routes.php | 0 .../taxonomy/Controller/Component/empty | 0 .../Controller/TaxonomyAppController.php | 14 + .../Controller/TaxonomyController.php | 19 + .../Controller/VocabulariesController.php | 200 + .../Fields/field_terms/Config/bootstrap.php | 0 .../Fields/field_terms/Config/routes.php | 0 .../field_terms/Controller/Component/empty | 0 .../Model/Behavior/FieldTermsHookBehavior.php | 156 + .../field_terms/Model/FieldTermsAppModel.php | 4 + .../Fields/field_terms/View/Elements/edit.ctp | 56 + .../View/Elements/formatter_from.ctp | 9 + .../View/Elements/settings_from.ctp | 35 + .../Fields/field_terms/View/Elements/view.ctp | 32 + .../View/Helper/FieldTermsHookHelper.php | 45 + .../Fields/field_terms/field_terms.yaml | 3 + app/Plugin/taxonomy/Lib/empty | 0 .../taxonomy/Locale/spa/LC_MESSAGES/core.po | 95 + app/Plugin/taxonomy/Model/Behavior/empty | 0 .../taxonomy/Model/TaxonomyAppModel.php | 14 + app/Plugin/taxonomy/Model/Term.php | 18 + app/Plugin/taxonomy/Model/Vocabulary.php | 67 + app/Plugin/taxonomy/View/Elements/help.ctp | 66 + .../taxonomy_vocabularies_settings.ctp | 31 + .../taxonomy/View/Elements/term_node.ctp | 12 + app/Plugin/taxonomy/View/Elements/toolbar.ctp | 6 + .../View/Helper/TaxonomyHookHelper.php | 128 + .../taxonomy/View/Vocabularies/admin_add.ctp | 10 + .../taxonomy/View/Vocabularies/admin_edit.ctp | 11 + .../View/Vocabularies/admin_edit_term.ctp | 13 + .../View/Vocabularies/admin_index.ctp | 30 + .../View/Vocabularies/admin_terms.ctp | 97 + app/Plugin/taxonomy/taxonomy.yaml | 5 + app/Plugin/taxonomy/webroot/css/empty | 0 app/Plugin/taxonomy/webroot/img/empty | 0 app/Plugin/taxonomy/webroot/js/empty | 0 app/Plugin/user/Config/bootstrap.php | 0 app/Plugin/user/Config/routes.php | 16 + .../Controller/Component/MailerComponent.php | 182 + .../Component/UserHookComponent.php | 18 + .../user/Controller/DisplayController.php | 57 + .../user/Controller/FieldsController.php | 52 + app/Plugin/user/Controller/ListController.php | 203 + .../user/Controller/PermissionsController.php | 114 + .../user/Controller/RolesController.php | 59 + .../user/Controller/UserAppController.php | 14 + app/Plugin/user/Controller/UserController.php | 236 + app/Plugin/user/Lib/empty | 0 .../user/Locale/eng/LC_MESSAGES/core.po | 19 + .../user/Locale/spa/LC_MESSAGES/core.po | 216 + .../user/Model/Behavior/UserHookBehavior.php | 27 + app/Plugin/user/Model/Role.php | 47 + app/Plugin/user/Model/User.php | 115 + app/Plugin/user/Model/UserAppModel.php | 14 + app/Plugin/user/View/Dashboard/index.ctp | 20 + app/Plugin/user/View/Dashboard/login.ctp | 21 + .../View/Display/admin_field_formatter.ctp | 14 + app/Plugin/user/View/Display/admin_index.ctp | 50 + app/Plugin/user/View/Elements/help.ctp | 16 + .../user/View/Elements/permission-node.ctp | 6 + app/Plugin/user/View/Elements/settings.ctp | 157 + app/Plugin/user/View/Elements/toolbar.ctp | 20 + .../user/View/Elements/user_login_block.ctp | 14 + .../user/View/Elements/user_new_block.ctp | 16 + .../View/Elements/user_new_block_settings.ctp | 14 + .../user/View/Fields/admin_field_settings.ctp | 17 + app/Plugin/user/View/Fields/admin_index.ctp | 108 + app/Plugin/user/View/Helper/TreeHelper.php | 456 ++ .../user/View/Helper/UserHookHelper.php | 45 + app/Plugin/user/View/List/admin_add.ctp | 30 + app/Plugin/user/View/List/admin_edit.ctp | 31 + app/Plugin/user/View/List/admin_index.ctp | 103 + .../user/View/Permissions/admin_edit.ctp | 17 + .../user/View/Permissions/admin_index.ctp | 6 + .../user/View/Permissions/admin_toggle.ctp | 1 + app/Plugin/user/View/Roles/admin_edit.ctp | 10 + app/Plugin/user/View/Roles/admin_index.ctp | 47 + app/Plugin/user/View/User/admin_login.ctp | 12 + app/Plugin/user/View/User/login.ctp | 11 + app/Plugin/user/View/User/my_account.ctp | 27 + .../user/View/User/password_recovery.ctp | 7 + app/Plugin/user/View/User/profile.ctp | 34 + app/Plugin/user/View/User/register.ctp | 25 + app/Plugin/user/user.yaml | 5 + app/Plugin/user/webroot/css/treeview.css | 55 + app/Plugin/user/webroot/img/allow-0.gif | Bin 0 -> 258 bytes app/Plugin/user/webroot/img/allow-1.gif | Bin 0 -> 263 bytes app/Plugin/user/webroot/img/icon_padlock.gif | Bin 0 -> 249 bytes app/Plugin/user/webroot/img/key.png | Bin 0 -> 612 bytes .../webroot/img/treeview-default-line.gif | Bin 0 -> 1993 bytes .../user/webroot/img/treeview-default.gif | Bin 0 -> 1222 bytes app/Plugin/user/webroot/js/acos.js | 20 + app/Plugin/user/webroot/js/jquery.cookie.js | 92 + app/Plugin/user/webroot/js/treeview.js | 256 + app/Vendor/PclZip.php | 5694 +++++++++++++++++ app/Vendor/Spyc.php | 1024 +++ app/Vendor/Upload.php | 5136 +++++++++++++++ app/Vendor/shells/tasks/empty | 0 app/Vendor/shells/templates/empty | 0 app/View/Elements/default_renderNode.ctp | 2 + app/View/Elements/default_theme_block.ctp | 11 + .../Elements/default_theme_breadcrumb.ctp | 18 + app/View/Emails/html/empty | 0 app/View/Emails/text/empty | 0 app/View/Errors/error400.ctp | 37 + app/View/Errors/error500.ctp | 30 + app/View/Errors/private_action.ctp | 32 + app/View/Helper/AppHelper.php | 258 + app/View/Helper/CustomHooksHelper.php | 18 + app/View/Helper/LayoutHelper.php | 1149 ++++ app/View/Helper/MenuHelper.php | 707 ++ app/View/Helper/QaFormHelper.php | 734 +++ app/View/Helper/QaHtmlHelper.php | 567 ++ app/View/Helper/TableHelper.php | 204 + app/View/Install/database.ctp | 59 + app/View/Install/finish.ctp | 8 + app/View/Install/license.ctp | 349 + app/View/Install/server_test.ctp | 23 + app/View/Install/user_account.ctp | 64 + app/View/Layouts/emails/html/empty | 0 app/View/Layouts/emails/text/empty | 0 app/View/Layouts/error.ctp | 14 + app/View/Layouts/install.ctp | 21 + app/View/Layouts/js/empty | 0 app/View/Layouts/rss.ctp | 34 + app/View/Layouts/rss/empty | 0 app/View/Layouts/xml/empty | 0 app/View/Scaffolds/empty | 0 .../Themed/AdminDefault/AdminDefault.yaml | 18 + .../Themed/AdminDefault/Layouts/default.ctp | 99 + .../theme_admin_default/Config/bootstrap.php | 0 .../theme_admin_default/Config/routes.php | 0 .../Controller/Component/empty | 0 .../Controller/SettingsController.php | 10 + .../Locale/spa/LC_MESSAGES/core.po | 22 + .../theme_admin_default/Model/Behavior/empty | 0 .../View/Elements/content-menu.ctp | 6 + .../View/Elements/settings_form.ctp | 0 .../Helper/ThemeAdminDefaultHookHelper.php | 93 + .../View/settings/admin_index.ctp | 0 app/View/Themed/AdminDefault/thumbnail.png | Bin 0 -> 6386 bytes .../Themed/AdminDefault/webroot/css/reset.css | 60 + .../AdminDefault/webroot/css/styles.css | 284 + .../Themed/AdminDefault/webroot/files/empty | 0 .../AdminDefault/webroot/img/boxes/alert.png | Bin 0 -> 5433 bytes .../AdminDefault/webroot/img/boxes/bubble.png | Bin 0 -> 5163 bytes .../AdminDefault/webroot/img/boxes/error.png | Bin 0 -> 5316 bytes .../webroot/img/boxes/success.png | Bin 0 -> 5251 bytes .../Themed/AdminDefault/webroot/img/down.gif | Bin 0 -> 57 bytes .../webroot/img/footer-top-bg.png | Bin 0 -> 159 bytes .../webroot/img/header-bottom-bg.png | Bin 0 -> 209 bytes .../webroot/img/menu-active-arrow.png | Bin 0 -> 191 bytes .../webroot/img/menu-nav-white.png | Bin 0 -> 2229 bytes .../AdminDefault/webroot/img/menu-nav.png | Bin 0 -> 2054 bytes .../AdminDefault/webroot/img/top-shadow.png | Bin 0 -> 182 bytes .../webroot/img/topmenu_arrow.png | Bin 0 -> 222 bytes .../Themed/AdminDefault/webroot/img/up.gif | Bin 0 -> 56 bytes app/View/Themed/AdminDefault/webroot/js/empty | 0 app/View/Themed/Default/Default.yaml | 24 + app/View/Themed/Default/Layouts/default.ctp | 139 + .../Plugin/theme_default/Config/bootstrap.php | 0 .../Plugin/theme_default/Config/routes.php | 0 .../theme_default/Controller/Component/empty | 0 .../Controller/SettingsController.php | 10 + .../Plugin/theme_default/Model/Behavior/empty | 0 .../View/Elements/settings_form.ctp | 32 + .../View/Elements/theme_default_slider.ctp | 51 + .../theme_default_slider_settings.ctp | 10 + .../View/Helper/ThemeDefaultHookHelper.php | 149 + app/View/Themed/Default/thumbnail.png | Bin 0 -> 20008 bytes .../Default/webroot/css/nivo-slider.css | 89 + app/View/Themed/Default/webroot/css/reset.css | 60 + .../Themed/Default/webroot/css/styles.css | 578 ++ app/View/Themed/Default/webroot/files/empty | 0 .../img/big-buttons/big-button-blue.png | Bin 0 -> 2765 bytes .../img/big-buttons/big-button-green.png | Bin 0 -> 2646 bytes .../img/big-buttons/big-button-orange.png | Bin 0 -> 2771 bytes .../img/big-buttons/big-button-purple.png | Bin 0 -> 2755 bytes .../img/big-buttons/big-button-red.png | Bin 0 -> 2708 bytes .../img/big-buttons/big-button-turquoise.png | Bin 0 -> 2782 bytes .../Default/webroot/img/boxes/alert.png | Bin 0 -> 5433 bytes .../Default/webroot/img/boxes/bubble.png | Bin 0 -> 5163 bytes .../Default/webroot/img/boxes/error.png | Bin 0 -> 5316 bytes .../Default/webroot/img/boxes/success.png | Bin 0 -> 5251 bytes .../Themed/Default/webroot/img/bubble.png | Bin 0 -> 2431 bytes .../Default/webroot/img/comment-quote.gif | Bin 0 -> 153 bytes .../Themed/Default/webroot/img/content-bg.png | Bin 0 -> 128 bytes app/View/Themed/Default/webroot/img/feed.png | Bin 0 -> 656 bytes .../Default/webroot/img/footer-top-bg.png | Bin 0 -> 159 bytes app/View/Themed/Default/webroot/img/gear.png | Bin 0 -> 4323 bytes .../Default/webroot/img/header-bottom-bg.png | Bin 0 -> 209 bytes .../Themed/Default/webroot/img/menu-nav.png | Bin 0 -> 2054 bytes app/View/Themed/Default/webroot/img/pen.png | Bin 0 -> 3313 bytes .../Themed/Default/webroot/img/quote-bg.png | Bin 0 -> 117 bytes .../Themed/Default/webroot/img/quote-icon.png | Bin 0 -> 786 bytes .../Default/webroot/img/quote-inner-bg.png | Bin 0 -> 162 bytes .../webroot/img/slider/slider-arrows.png | Bin 0 -> 824 bytes .../webroot/img/slider/slider-bullets.png | Bin 0 -> 1322 bytes .../webroot/img/slider/slider-loading.gif | Bin 0 -> 8238 bytes .../webroot/img/slider/slider-shadow.jpg | Bin 0 -> 3048 bytes .../img/small-buttons/small-button-black.png | Bin 0 -> 1793 bytes .../img/small-buttons/small-button-blue.png | Bin 0 -> 2099 bytes .../img/small-buttons/small-button-green.png | Bin 0 -> 1981 bytes .../small-buttons/small-button-lightblue.png | Bin 0 -> 2127 bytes .../img/small-buttons/small-button-orange.png | Bin 0 -> 2035 bytes .../img/small-buttons/small-button-pink.png | Bin 0 -> 2035 bytes .../img/small-buttons/small-button-purple.png | Bin 0 -> 2062 bytes .../img/small-buttons/small-button-red.png | Bin 0 -> 2009 bytes .../img/small-buttons/small-button-silver.png | Bin 0 -> 1727 bytes .../img/small-buttons/small-button-teal.png | Bin 0 -> 2120 bytes .../Themed/Default/webroot/img/top-shadow.png | Bin 0 -> 182 bytes .../webroot/js/Colaborate-Thin_400.font.js | 19 + .../Themed/Default/webroot/js/cufon-yui.js | 7 + .../Themed/Default/webroot/js/farbtastic.js | 345 + .../Default/webroot/js/farbtastic/LICENSE.txt | 341 + .../webroot/js/farbtastic/farbtastic.css | 51 + .../webroot/js/farbtastic/farbtastic.js | 345 + .../Default/webroot/js/farbtastic/marker.png | Bin 0 -> 652 bytes .../Default/webroot/js/farbtastic/mask.png | Bin 0 -> 2020 bytes .../Default/webroot/js/farbtastic/wheel.png | Bin 0 -> 11733 bytes .../js/nivo-slider/jquery.nivo.slider.pack.js | 67 + .../webroot/js/nivo-slider/license.txt | 22 + app/index.php | 17 + app/tmp/cache/i18n/empty | 0 app/tmp/cache/installer/empty | 0 app/tmp/cache/models/empty | 0 app/tmp/cache/persistent/empty | 0 app/tmp/cache/views/empty | 0 app/tmp/logs/empty | 0 app/tmp/sessions/empty | 0 app/webroot/.htaccess | 6 + app/webroot/css/error.css | 26 + app/webroot/css/install.css | 117 + app/webroot/css/reset.css | 60 + app/webroot/favicon.ico | Bin 0 -> 1150 bytes app/webroot/files/slider/1_eng.jpg | Bin 0 -> 132556 bytes app/webroot/files/slider/1_spa.jpg | Bin 0 -> 134505 bytes app/webroot/files/slider/2_eng.jpg | Bin 0 -> 67625 bytes app/webroot/files/slider/2_spa.jpg | Bin 0 -> 67625 bytes app/webroot/files/slider/3_eng.jpg | Bin 0 -> 20702 bytes app/webroot/files/slider/3_spa.jpg | Bin 0 -> 20702 bytes app/webroot/img/accept.png | Bin 0 -> 781 bytes app/webroot/img/anonymous_avatar.jpg | Bin 0 -> 1591 bytes app/webroot/img/boxes/alert.png | Bin 0 -> 5433 bytes app/webroot/img/boxes/bubble.png | Bin 0 -> 5163 bytes app/webroot/img/boxes/error.png | Bin 0 -> 5316 bytes app/webroot/img/boxes/success.png | Bin 0 -> 5251 bytes app/webroot/img/error.png | Bin 0 -> 666 bytes app/webroot/img/logo.png | Bin 0 -> 7277 bytes app/webroot/index.php | 81 + app/webroot/js/jquery.js | 16 + app/webroot/js/json.js | 96 + app/webroot/js/quickapps.js | 223 + .../ui-bg_diagonals-thick_18_b81900_40x40.png | Bin 0 -> 260 bytes .../ui-bg_diagonals-thick_20_666666_40x40.png | Bin 0 -> 251 bytes .../images/ui-bg_flat_10_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_100_f6f6f6_1x400.png | Bin 0 -> 104 bytes .../images/ui-bg_glass_100_fdf5ce_1x400.png | Bin 0 -> 125 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../ui-bg_gloss-wave_35_f6a828_500x100.png | Bin 0 -> 3762 bytes .../ui-bg_highlight-soft_100_eeeeee_1x100.png | Bin 0 -> 90 bytes .../ui-bg_highlight-soft_75_ffe45c_1x100.png | Bin 0 -> 167 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_228ef1_256x240.png | Bin 0 -> 5355 bytes .../images/ui-icons_ef8c08_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffd27a_256x240.png | Bin 0 -> 5355 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4369 bytes app/webroot/js/ui/css/ui-lightness/styles.css | 573 ++ app/webroot/js/ui/jquery-ui.js | 783 +++ app/webroot/js/vendors.php | 42 + index.php | 46 + 1112 files changed, 118928 insertions(+) create mode 100644 .htaccess create mode 100644 LICENSE create mode 100644 Modules/empty create mode 100644 README.mdown create mode 100644 app/.htaccess create mode 100644 app/Cake/Cache/Cache.php create mode 100644 app/Cake/Cache/Engine/ApcEngine.php create mode 100644 app/Cake/Cache/Engine/FileEngine.php create mode 100644 app/Cake/Cache/Engine/MemcacheEngine.php create mode 100644 app/Cake/Cache/Engine/WincacheEngine.php create mode 100644 app/Cake/Cache/Engine/XcacheEngine.php create mode 100644 app/Cake/Config/config.php create mode 100644 app/Cake/Config/routes.php create mode 100644 app/Cake/Config/unicode/casefolding/0080_00ff.php create mode 100644 app/Cake/Config/unicode/casefolding/0100_017f.php create mode 100644 app/Cake/Config/unicode/casefolding/0180_024F.php create mode 100644 app/Cake/Config/unicode/casefolding/0250_02af.php create mode 100644 app/Cake/Config/unicode/casefolding/0370_03ff.php create mode 100644 app/Cake/Config/unicode/casefolding/0400_04ff.php create mode 100644 app/Cake/Config/unicode/casefolding/0500_052f.php create mode 100644 app/Cake/Config/unicode/casefolding/0530_058f.php create mode 100644 app/Cake/Config/unicode/casefolding/1e00_1eff.php create mode 100644 app/Cake/Config/unicode/casefolding/1f00_1fff.php create mode 100644 app/Cake/Config/unicode/casefolding/2100_214f.php create mode 100644 app/Cake/Config/unicode/casefolding/2150_218f.php create mode 100644 app/Cake/Config/unicode/casefolding/2460_24ff.php create mode 100644 app/Cake/Config/unicode/casefolding/2c00_2c5f.php create mode 100644 app/Cake/Config/unicode/casefolding/2c60_2c7f.php create mode 100644 app/Cake/Config/unicode/casefolding/2c80_2cff.php create mode 100644 app/Cake/Config/unicode/casefolding/ff00_ffef.php create mode 100644 app/Cake/Configure/IniReader.php create mode 100644 app/Cake/Configure/PhpReader.php create mode 100644 app/Cake/Controller/AppController.php create mode 100644 app/Cake/Controller/CakeErrorController.php create mode 100644 app/Cake/Controller/Component.php create mode 100644 app/Cake/Controller/Component/AclComponent.php create mode 100644 app/Cake/Controller/Component/Auth/ActionsAuthorize.php create mode 100644 app/Cake/Controller/Component/Auth/BaseAuthenticate.php create mode 100644 app/Cake/Controller/Component/Auth/BaseAuthorize.php create mode 100644 app/Cake/Controller/Component/Auth/BasicAuthenticate.php create mode 100644 app/Cake/Controller/Component/Auth/ControllerAuthorize.php create mode 100644 app/Cake/Controller/Component/Auth/CrudAuthorize.php create mode 100644 app/Cake/Controller/Component/Auth/DigestAuthenticate.php create mode 100644 app/Cake/Controller/Component/Auth/FormAuthenticate.php create mode 100644 app/Cake/Controller/Component/AuthComponent.php create mode 100644 app/Cake/Controller/Component/CookieComponent.php create mode 100644 app/Cake/Controller/Component/EmailComponent.php create mode 100644 app/Cake/Controller/Component/PaginatorComponent.php create mode 100644 app/Cake/Controller/Component/RequestHandlerComponent.php create mode 100644 app/Cake/Controller/Component/SecurityComponent.php create mode 100644 app/Cake/Controller/Component/SessionComponent.php create mode 100644 app/Cake/Controller/ComponentCollection.php create mode 100644 app/Cake/Controller/Controller.php create mode 100644 app/Cake/Controller/PagesController.php create mode 100644 app/Cake/Controller/Scaffold.php create mode 100644 app/Cake/Core/App.php create mode 100644 app/Cake/Core/CakePlugin.php create mode 100644 app/Cake/Core/Configure.php create mode 100644 app/Cake/Core/Object.php create mode 100644 app/Cake/Error/ErrorHandler.php create mode 100644 app/Cake/Error/ExceptionRenderer.php create mode 100644 app/Cake/Error/exceptions.php create mode 100644 app/Cake/I18n/I18n.php create mode 100644 app/Cake/I18n/L10n.php create mode 100644 app/Cake/I18n/Multibyte.php create mode 100644 app/Cake/LICENSE.txt create mode 100644 app/Cake/Log/CakeLog.php create mode 100644 app/Cake/Log/CakeLogInterface.php create mode 100644 app/Cake/Log/Engine/FileLog.php create mode 100644 app/Cake/Model/AclNode.php create mode 100644 app/Cake/Model/Aco.php create mode 100644 app/Cake/Model/AcoAction.php create mode 100644 app/Cake/Model/AppModel.php create mode 100644 app/Cake/Model/Aro.php create mode 100644 app/Cake/Model/Behavior/AclBehavior.php create mode 100644 app/Cake/Model/Behavior/ContainableBehavior.php create mode 100644 app/Cake/Model/Behavior/TranslateBehavior.php create mode 100644 app/Cake/Model/Behavior/TreeBehavior.php create mode 100644 app/Cake/Model/BehaviorCollection.php create mode 100644 app/Cake/Model/CakeSchema.php create mode 100644 app/Cake/Model/ConnectionManager.php create mode 100644 app/Cake/Model/Datasource/CakeSession.php create mode 100644 app/Cake/Model/Datasource/DataSource.php create mode 100644 app/Cake/Model/Datasource/Database/Mysql.php create mode 100644 app/Cake/Model/Datasource/Database/Postgres.php create mode 100644 app/Cake/Model/Datasource/Database/Sqlite.php create mode 100644 app/Cake/Model/Datasource/Database/Sqlserver.php create mode 100644 app/Cake/Model/Datasource/DboSource.php create mode 100644 app/Cake/Model/Datasource/Session/CacheSession.php create mode 100644 app/Cake/Model/Datasource/Session/DatabaseSession.php create mode 100644 app/Cake/Model/Model.php create mode 100644 app/Cake/Model/ModelBehavior.php create mode 100644 app/Cake/Model/Permission.php create mode 100644 app/Cake/Network/CakeRequest.php create mode 100644 app/Cake/Network/CakeResponse.php create mode 100644 app/Cake/Network/CakeSocket.php create mode 100644 app/Cake/Network/Email/AbstractTransport.php create mode 100644 app/Cake/Network/Email/CakeEmail.php create mode 100644 app/Cake/Network/Email/DebugTransport.php create mode 100644 app/Cake/Network/Email/MailTransport.php create mode 100644 app/Cake/Network/Email/SmtpTransport.php create mode 100644 app/Cake/Network/Http/BasicAuthentication.php create mode 100644 app/Cake/Network/Http/DigestAuthentication.php create mode 100644 app/Cake/Network/Http/HttpResponse.php create mode 100644 app/Cake/Network/Http/HttpSocket.php create mode 100644 app/Cake/Routing/Dispatcher.php create mode 100644 app/Cake/Routing/Route/CakeRoute.php create mode 100644 app/Cake/Routing/Route/PluginShortRoute.php create mode 100644 app/Cake/Routing/Route/RedirectRoute.php create mode 100644 app/Cake/Routing/Router.php create mode 100644 app/Cake/Utility/ClassRegistry.php create mode 100644 app/Cake/Utility/Debugger.php create mode 100644 app/Cake/Utility/File.php create mode 100644 app/Cake/Utility/Folder.php create mode 100644 app/Cake/Utility/Inflector.php create mode 100644 app/Cake/Utility/ObjectCollection.php create mode 100644 app/Cake/Utility/Sanitize.php create mode 100644 app/Cake/Utility/Security.php create mode 100644 app/Cake/Utility/Set.php create mode 100644 app/Cake/Utility/String.php create mode 100644 app/Cake/Utility/Validation.php create mode 100644 app/Cake/Utility/Xml.php create mode 100644 app/Cake/VERSION.txt create mode 100644 app/Cake/View/Elements/exception_stack_trace.ctp create mode 100644 app/Cake/View/Elements/sql_dump.ctp create mode 100644 app/Cake/View/Emails/html/default.ctp create mode 100644 app/Cake/View/Emails/text/default.ctp create mode 100644 app/Cake/View/Errors/error400.ctp create mode 100644 app/Cake/View/Errors/error500.ctp create mode 100644 app/Cake/View/Errors/missing_action.ctp create mode 100644 app/Cake/View/Errors/missing_behavior_class.ctp create mode 100644 app/Cake/View/Errors/missing_behavior_file.ctp create mode 100644 app/Cake/View/Errors/missing_component_class.ctp create mode 100644 app/Cake/View/Errors/missing_component_file.ctp create mode 100644 app/Cake/View/Errors/missing_connection.ctp create mode 100644 app/Cake/View/Errors/missing_controller.ctp create mode 100644 app/Cake/View/Errors/missing_database.ctp create mode 100644 app/Cake/View/Errors/missing_datasource_config.ctp create mode 100644 app/Cake/View/Errors/missing_datasource_file.ctp create mode 100644 app/Cake/View/Errors/missing_helper_class.ctp create mode 100644 app/Cake/View/Errors/missing_helper_file.ctp create mode 100644 app/Cake/View/Errors/missing_layout.ctp create mode 100644 app/Cake/View/Errors/missing_plugin.ctp create mode 100644 app/Cake/View/Errors/missing_table.ctp create mode 100644 app/Cake/View/Errors/missing_view.ctp create mode 100644 app/Cake/View/Errors/pdo_error.ctp create mode 100644 app/Cake/View/Errors/private_action.ctp create mode 100644 app/Cake/View/Errors/scaffold_error.ctp create mode 100644 app/Cake/View/Helper.php create mode 100644 app/Cake/View/Helper/AppHelper.php create mode 100644 app/Cake/View/Helper/CacheHelper.php create mode 100644 app/Cake/View/Helper/FormHelper.php create mode 100644 app/Cake/View/Helper/HtmlHelper.php create mode 100644 app/Cake/View/Helper/JqueryEngineHelper.php create mode 100644 app/Cake/View/Helper/JsBaseEngineHelper.php create mode 100644 app/Cake/View/Helper/JsHelper.php create mode 100644 app/Cake/View/Helper/MootoolsEngineHelper.php create mode 100644 app/Cake/View/Helper/NumberHelper.php create mode 100644 app/Cake/View/Helper/PaginatorHelper.php create mode 100644 app/Cake/View/Helper/PrototypeEngineHelper.php create mode 100644 app/Cake/View/Helper/RssHelper.php create mode 100644 app/Cake/View/Helper/SessionHelper.php create mode 100644 app/Cake/View/Helper/TextHelper.php create mode 100644 app/Cake/View/Helper/TimeHelper.php create mode 100644 app/Cake/View/HelperCollection.php create mode 100644 app/Cake/View/Layouts/Emails/html/default.ctp create mode 100644 app/Cake/View/Layouts/Emails/text/default.ctp create mode 100644 app/Cake/View/Layouts/ajax.ctp create mode 100644 app/Cake/View/Layouts/default.ctp create mode 100644 app/Cake/View/Layouts/flash.ctp create mode 100644 app/Cake/View/Layouts/js/default.ctp create mode 100644 app/Cake/View/Layouts/rss/default.ctp create mode 100644 app/Cake/View/Layouts/xml/default.ctp create mode 100644 app/Cake/View/MediaView.php create mode 100644 app/Cake/View/Pages/home.ctp create mode 100644 app/Cake/View/ScaffoldView.php create mode 100644 app/Cake/View/Scaffolds/form.ctp create mode 100644 app/Cake/View/Scaffolds/index.ctp create mode 100644 app/Cake/View/Scaffolds/view.ctp create mode 100644 app/Cake/View/ThemeView.php create mode 100644 app/Cake/View/View.php create mode 100644 app/Cake/basics.php create mode 100644 app/Cake/bootstrap.php create mode 100644 app/Config/Schema/manual/README.mdown create mode 100644 app/Config/Schema/manual/install create mode 100644 app/Config/Schema/manual/quickapps.install.sql create mode 100644 app/Config/Schema/tables/acos.sql create mode 100644 app/Config/Schema/tables/aros.sql create mode 100644 app/Config/Schema/tables/aros_acos.sql create mode 100644 app/Config/Schema/tables/block_custom.sql create mode 100644 app/Config/Schema/tables/block_regions.sql create mode 100644 app/Config/Schema/tables/block_roles.sql create mode 100644 app/Config/Schema/tables/blocks.sql create mode 100644 app/Config/Schema/tables/comments.sql create mode 100644 app/Config/Schema/tables/field_data.sql create mode 100644 app/Config/Schema/tables/fields.sql create mode 100644 app/Config/Schema/tables/i18n.sql create mode 100644 app/Config/Schema/tables/languages.sql create mode 100644 app/Config/Schema/tables/menu_links.sql create mode 100644 app/Config/Schema/tables/menus.sql create mode 100644 app/Config/Schema/tables/modules.sql create mode 100644 app/Config/Schema/tables/node_types.sql create mode 100644 app/Config/Schema/tables/nodes.sql create mode 100644 app/Config/Schema/tables/nodes_roles.sql create mode 100644 app/Config/Schema/tables/nodes_terms.sql create mode 100644 app/Config/Schema/tables/roles.sql create mode 100644 app/Config/Schema/tables/terms.sql create mode 100644 app/Config/Schema/tables/translations.sql create mode 100644 app/Config/Schema/tables/users.sql create mode 100644 app/Config/Schema/tables/users_roles.sql create mode 100644 app/Config/Schema/tables/variables.sql create mode 100644 app/Config/Schema/tables/vocabularies.sql create mode 100644 app/Config/bootstrap.php create mode 100644 app/Config/core.php create mode 100644 app/Config/database.php.install create mode 100644 app/Config/email.php.default create mode 100644 app/Config/routes.php create mode 100644 app/Controller/AppController.php create mode 100644 app/Controller/Component/HookComponent.php create mode 100644 app/Controller/Component/InstallerComponent.php create mode 100644 app/Controller/Component/QuickappsComponent.php create mode 100644 app/Controller/InstallController.php create mode 100644 app/Lib/Model/Model.php create mode 100644 app/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Model/AppModel.php create mode 100644 app/Model/Behavior/SerializedBehavior.php create mode 100644 app/Model/Behavior/SluggableBehavior.php create mode 100644 app/Model/Behavior/WhoDidItBehavior.php create mode 100644 app/Model/Datasource/empty create mode 100644 app/Plugin/block/Config/bootstrap.php create mode 100644 app/Plugin/block/Config/routes.php create mode 100644 app/Plugin/block/Controller/BlockAppController.php create mode 100644 app/Plugin/block/Controller/BlockController.php create mode 100644 app/Plugin/block/Controller/Component/BlockHookComponent.php create mode 100644 app/Plugin/block/Controller/ManageController.php create mode 100644 app/Plugin/block/Lib/empty create mode 100644 app/Plugin/block/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/block/Model/Behavior/BlockHookBehavior.php create mode 100644 app/Plugin/block/Model/Block.php create mode 100644 app/Plugin/block/Model/BlockAppModel.php create mode 100644 app/Plugin/block/Model/BlockCustom.php create mode 100644 app/Plugin/block/Model/BlockRegion.php create mode 100644 app/Plugin/block/Model/BlockRole.php create mode 100644 app/Plugin/block/View/Elements/help.ctp create mode 100644 app/Plugin/block/View/Elements/toolbar.ctp create mode 100644 app/Plugin/block/View/Helper/BlockHookHelper.php create mode 100644 app/Plugin/block/View/Manage/admin_add.ctp create mode 100644 app/Plugin/block/View/Manage/admin_edit.ctp create mode 100644 app/Plugin/block/View/Manage/admin_index.ctp create mode 100644 app/Plugin/block/block.yaml create mode 100644 app/Plugin/block/webroot/css/empty create mode 100644 app/Plugin/block/webroot/img/empty create mode 100644 app/Plugin/block/webroot/js/empty create mode 100644 app/Plugin/comment/Config/bootstrap.php create mode 100644 app/Plugin/comment/Config/routes.php create mode 100644 app/Plugin/comment/Controller/CommentAppController.php create mode 100644 app/Plugin/comment/Controller/CommentController.php create mode 100644 app/Plugin/comment/Controller/Component/empty create mode 100644 app/Plugin/comment/Controller/PublishedController.php create mode 100644 app/Plugin/comment/Controller/UnpublishedController.php create mode 100644 app/Plugin/comment/Lib/Nbbc.php create mode 100644 app/Plugin/comment/Lib/nbbc/CHANGELOG create mode 100644 app/Plugin/comment/Lib/nbbc/LICENSE create mode 100644 app/Plugin/comment/Lib/nbbc/nbbc.php create mode 100644 app/Plugin/comment/Lib/nbbc/powered_by_nbbc.png create mode 100644 app/Plugin/comment/Lib/nbbc/powered_by_nbbc_small.png create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/angry.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/anime.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/bigeyes.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/bigsmile.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/bigwink.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/blue.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/boggle.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/confuse.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/cool.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/evil.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/frown.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/heart.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/irritated.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/laugh.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/lookleft.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/lookright.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/neutral.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/saint.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/sleepy.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/smile.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/smile3.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/sneaky.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/star.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/surprise.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/sweatdrop.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/teeth.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/tongue.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/wink.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/wink3.gif create mode 100644 app/Plugin/comment/Lib/nbbc/smileys/worry.gif create mode 100644 app/Plugin/comment/Lib/nbbc/src/nbbc_email.php create mode 100644 app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php create mode 100644 app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php create mode 100644 app/Plugin/comment/Lib/nbbc/src/nbbc_main.php create mode 100644 app/Plugin/comment/Lib/nbbc/src/nbbc_parse.php create mode 100644 app/Plugin/comment/Lib/nbbc/test_nbbc.php create mode 100644 app/Plugin/comment/Lib/nbbc/tools/Makefile create mode 100644 app/Plugin/comment/Lib/nbbc/tools/collect_smileys.php create mode 100644 app/Plugin/comment/Lib/nbbc/tools/merge.pl create mode 100644 app/Plugin/comment/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/comment/Model/Behavior/BBCodeBehavior.php create mode 100644 app/Plugin/comment/Model/Comment.php create mode 100644 app/Plugin/comment/Model/CommentAppModel.php create mode 100644 app/Plugin/comment/View/Elements/help.ctp create mode 100644 app/Plugin/comment/View/Elements/toolbar.ctp create mode 100644 app/Plugin/comment/View/Helper/CommentHookHelper.php create mode 100644 app/Plugin/comment/View/Published/admin_index.ctp create mode 100644 app/Plugin/comment/View/Unpublished/admin_index.ctp create mode 100644 app/Plugin/comment/comment.yaml create mode 100644 app/Plugin/comment/webroot/css/empty create mode 100644 app/Plugin/comment/webroot/img/empty create mode 100644 app/Plugin/comment/webroot/js/jquery.scrollTo-min.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/jquery.markitup.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/locale/eng.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/locale/spa.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/bold.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/clean.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/code.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/fonts.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/italic.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/link.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/list-bullet.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/list-item.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/list-numeric.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/picture.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/preview.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/quotes.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/stroke.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/underline.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/images/video.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/readme.txt create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/set.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/bbcode/style.css create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/bold.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/clean.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/image.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/italic.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/link.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/list-bullet.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/list-numeric.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/picture.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/preview.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/images/stroke.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/set.js create mode 100644 app/Plugin/comment/webroot/js/markItUp/sets/default/style.css create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-container.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-bbcode.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-dotclear.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-html.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-json.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-markdown.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-textile.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-wiki.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor-xml.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/bg-editor.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/handle.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/menu.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/images/submenu.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/markitup/style.css create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/simple/images/handle.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/simple/images/menu.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/simple/images/submenu.png create mode 100644 app/Plugin/comment/webroot/js/markItUp/skins/simple/style.css create mode 100644 app/Plugin/field/Config/bootstrap.php create mode 100644 app/Plugin/field/Config/routes.php create mode 100644 app/Plugin/field/Controller/Component/FieldHookComponent.php create mode 100644 app/Plugin/field/Controller/FieldAppController.php create mode 100644 app/Plugin/field/Controller/HandlerController.php create mode 100644 app/Plugin/field/Fields/field_file/Config/bootstrap.php create mode 100644 app/Plugin/field/Fields/field_file/Config/routes.php create mode 100644 app/Plugin/field/Fields/field_file/Controller/Component/empty create mode 100644 app/Plugin/field/Fields/field_file/Controller/FieldFileAppController.php create mode 100644 app/Plugin/field/Fields/field_file/Controller/UploadifyController.php create mode 100644 app/Plugin/field/Fields/field_file/Lib/FieldFile.php create mode 100644 app/Plugin/field/Fields/field_file/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/field/Fields/field_file/Model/Behavior/FieldFileHookBehavior.php create mode 100644 app/Plugin/field/Fields/field_file/Model/FieldFileAppModel.php create mode 100644 app/Plugin/field/Fields/field_file/View/Elements/edit.ctp create mode 100644 app/Plugin/field/Fields/field_file/View/Elements/formatter_from.ctp create mode 100644 app/Plugin/field/Fields/field_file/View/Elements/settings_from.ctp create mode 100644 app/Plugin/field/Fields/field_file/View/Elements/view.ctp create mode 100644 app/Plugin/field/Fields/field_file/View/Helper/FieldFileHookHelper.php create mode 100644 app/Plugin/field/Fields/field_file/field_file.yaml create mode 100644 app/Plugin/field/Fields/field_file/webroot/css/field_file.css create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/application-octet-stream.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/application-pdf.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/application-x-executable.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/audio-x-generic.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/image-x-generic.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/package-x-generic.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/text-html.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/text-plain.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/text-x-generic.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/text-x-script.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/video-x-generic.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/x-office-document.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/x-office-presentation.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/img/icons/x-office-spreadsheet.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/field_file.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/locale.eng.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/locale.spa.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/cancel.png create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/check.php create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/expressInstall.swf create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/jquery-1.4.2.min.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/jquery.uploadify.v2.1.4.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/jquery.uploadify.v2.1.4.min.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/swfobject.js create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/uploadify.allglyphs.swf create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/uploadify.css create mode 100644 app/Plugin/field/Fields/field_file/webroot/js/uploadify/uploadify.swf create mode 100644 app/Plugin/field/Fields/field_list/Config/bootstrap.php create mode 100644 app/Plugin/field/Fields/field_list/Config/routes.php create mode 100644 app/Plugin/field/Fields/field_list/Controller/Component/empty create mode 100644 app/Plugin/field/Fields/field_list/Model/Behavior/FieldListHookBehavior.php create mode 100644 app/Plugin/field/Fields/field_list/Model/FieldListAppModel.php create mode 100644 app/Plugin/field/Fields/field_list/View/Elements/edit.ctp create mode 100644 app/Plugin/field/Fields/field_list/View/Elements/formatter_from.ctp create mode 100644 app/Plugin/field/Fields/field_list/View/Elements/settings_from.ctp create mode 100644 app/Plugin/field/Fields/field_list/View/Elements/view.ctp create mode 100644 app/Plugin/field/Fields/field_list/View/Helper/FieldListHookHelper.php create mode 100644 app/Plugin/field/Fields/field_list/field_list.yaml create mode 100644 app/Plugin/field/Fields/field_text/Config/bootstrap.php create mode 100644 app/Plugin/field/Fields/field_text/Config/routes.php create mode 100644 app/Plugin/field/Fields/field_text/Controller/Component/empty create mode 100644 app/Plugin/field/Fields/field_text/Lib/Html2text.php create mode 100644 app/Plugin/field/Fields/field_text/Lib/Markdown.php create mode 100644 app/Plugin/field/Fields/field_text/Model/Behavior/FieldTextHookBehavior.php create mode 100644 app/Plugin/field/Fields/field_text/Model/FieldTextAppModel.php create mode 100644 app/Plugin/field/Fields/field_text/View/Elements/edit.ctp create mode 100644 app/Plugin/field/Fields/field_text/View/Elements/formatter_from.ctp create mode 100644 app/Plugin/field/Fields/field_text/View/Elements/settings_from.ctp create mode 100644 app/Plugin/field/Fields/field_text/View/Elements/view.ctp create mode 100644 app/Plugin/field/Fields/field_text/View/Helper/FieldTextHookHelper.php create mode 100644 app/Plugin/field/Fields/field_text/field_text.yaml create mode 100644 app/Plugin/field/Lib/empty create mode 100644 app/Plugin/field/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/field/Model/Behavior/FieldHookBehavior.php create mode 100644 app/Plugin/field/Model/Behavior/FieldableBehavior.php create mode 100644 app/Plugin/field/Model/Field.php create mode 100644 app/Plugin/field/Model/FieldAppModel.php create mode 100644 app/Plugin/field/Model/FieldData.php create mode 100644 app/Plugin/field/View/Elements/help.ctp create mode 100644 app/Plugin/field/View/Helper/empty create mode 100644 app/Plugin/field/field.yaml create mode 100644 app/Plugin/field/webroot/css/empty create mode 100644 app/Plugin/field/webroot/img/empty create mode 100644 app/Plugin/field/webroot/js/empty create mode 100644 app/Plugin/locale/Config/bootstrap.php create mode 100644 app/Plugin/locale/Config/routes.php create mode 100644 app/Plugin/locale/Controller/Component/empty create mode 100644 app/Plugin/locale/Controller/LanguagesController.php create mode 100644 app/Plugin/locale/Controller/LocaleAppController.php create mode 100644 app/Plugin/locale/Controller/LocaleController.php create mode 100644 app/Plugin/locale/Controller/PackagesController.php create mode 100644 app/Plugin/locale/Controller/TranslationsController.php create mode 100644 app/Plugin/locale/Lib/Locale.php create mode 100644 app/Plugin/locale/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/locale/Model/Behavior/empty create mode 100644 app/Plugin/locale/Model/Internationalization.php create mode 100644 app/Plugin/locale/Model/Language.php create mode 100644 app/Plugin/locale/Model/LocaleAppModel.php create mode 100644 app/Plugin/locale/Model/Translation.php create mode 100644 app/Plugin/locale/View/Elements/help.ctp create mode 100644 app/Plugin/locale/View/Elements/locale_language_switcher.ctp create mode 100644 app/Plugin/locale/View/Elements/locale_language_switcher_settings.ctp create mode 100644 app/Plugin/locale/View/Elements/toolbar.ctp create mode 100644 app/Plugin/locale/View/Helper/LocaleHookHelper.php create mode 100644 app/Plugin/locale/View/Languages/admin_edit.ctp create mode 100644 app/Plugin/locale/View/Languages/admin_index.ctp create mode 100644 app/Plugin/locale/View/Packages/admin_index.ctp create mode 100644 app/Plugin/locale/View/Translations/admin_add.ctp create mode 100644 app/Plugin/locale/View/Translations/admin_edit.ctp create mode 100644 app/Plugin/locale/View/Translations/admin_list.ctp create mode 100644 app/Plugin/locale/locale.yaml create mode 100644 app/Plugin/locale/webroot/css/empty create mode 100644 app/Plugin/locale/webroot/img/default.png create mode 100644 app/Plugin/locale/webroot/img/flags/ad.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ae.gif create mode 100644 app/Plugin/locale/webroot/img/flags/af.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ag.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ai.gif create mode 100644 app/Plugin/locale/webroot/img/flags/al.gif create mode 100644 app/Plugin/locale/webroot/img/flags/am.gif create mode 100644 app/Plugin/locale/webroot/img/flags/an.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ao.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ar.gif create mode 100644 app/Plugin/locale/webroot/img/flags/as.gif create mode 100644 app/Plugin/locale/webroot/img/flags/at.gif create mode 100644 app/Plugin/locale/webroot/img/flags/au.gif create mode 100644 app/Plugin/locale/webroot/img/flags/aw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ax.gif create mode 100644 app/Plugin/locale/webroot/img/flags/az.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ba.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bb.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bd.gif create mode 100644 app/Plugin/locale/webroot/img/flags/be.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bi.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bj.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bo.gif create mode 100644 app/Plugin/locale/webroot/img/flags/br.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bs.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/by.gif create mode 100644 app/Plugin/locale/webroot/img/flags/bz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ca.gif create mode 100644 app/Plugin/locale/webroot/img/flags/catalonia.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cd.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ch.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ci.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ck.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/co.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cs.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cx.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cy.gif create mode 100644 app/Plugin/locale/webroot/img/flags/cz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/de.gif create mode 100644 app/Plugin/locale/webroot/img/flags/dj.gif create mode 100644 app/Plugin/locale/webroot/img/flags/dk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/dm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/do.gif create mode 100644 app/Plugin/locale/webroot/img/flags/dz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ec.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ee.gif create mode 100644 app/Plugin/locale/webroot/img/flags/eg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/eh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/england.gif create mode 100644 app/Plugin/locale/webroot/img/flags/er.gif create mode 100644 app/Plugin/locale/webroot/img/flags/es.gif create mode 100644 app/Plugin/locale/webroot/img/flags/et.gif create mode 100644 app/Plugin/locale/webroot/img/flags/europeanunion.gif create mode 100644 app/Plugin/locale/webroot/img/flags/eus.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fam.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fi.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fj.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fo.gif create mode 100644 app/Plugin/locale/webroot/img/flags/fr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ga.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gb.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gd.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ge.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gi.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gp.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gq.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gs.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/gy.gif create mode 100644 app/Plugin/locale/webroot/img/flags/hk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/hm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/hn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/hr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ht.gif create mode 100644 app/Plugin/locale/webroot/img/flags/hu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/id.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ie.gif create mode 100644 app/Plugin/locale/webroot/img/flags/il.gif create mode 100644 app/Plugin/locale/webroot/img/flags/in.gif create mode 100644 app/Plugin/locale/webroot/img/flags/io.gif create mode 100644 app/Plugin/locale/webroot/img/flags/iq.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ir.gif create mode 100644 app/Plugin/locale/webroot/img/flags/is.gif create mode 100644 app/Plugin/locale/webroot/img/flags/it.gif create mode 100644 app/Plugin/locale/webroot/img/flags/jm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/jo.gif create mode 100644 app/Plugin/locale/webroot/img/flags/jp.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ke.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ki.gif create mode 100644 app/Plugin/locale/webroot/img/flags/km.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kp.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ky.gif create mode 100644 app/Plugin/locale/webroot/img/flags/kz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/la.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lb.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/li.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ls.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/lv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ly.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ma.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/md.gif create mode 100644 app/Plugin/locale/webroot/img/flags/me.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ml.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mo.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mp.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mq.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ms.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mx.gif create mode 100644 app/Plugin/locale/webroot/img/flags/my.gif create mode 100644 app/Plugin/locale/webroot/img/flags/mz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/na.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ne.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ng.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ni.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/no.gif create mode 100644 app/Plugin/locale/webroot/img/flags/np.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/nz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/om.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pa.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pe.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ph.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ps.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/pw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/py.gif create mode 100644 app/Plugin/locale/webroot/img/flags/qa.gif create mode 100644 app/Plugin/locale/webroot/img/flags/re.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ro.gif create mode 100644 app/Plugin/locale/webroot/img/flags/rs.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ru.gif create mode 100644 app/Plugin/locale/webroot/img/flags/rw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sa.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sb.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/scotland.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sd.gif create mode 100644 app/Plugin/locale/webroot/img/flags/se.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sh.gif create mode 100644 app/Plugin/locale/webroot/img/flags/si.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sj.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/so.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/st.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sy.gif create mode 100644 app/Plugin/locale/webroot/img/flags/sz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/td.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/th.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tj.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tk.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tl.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/to.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tr.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tv.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tw.gif create mode 100644 app/Plugin/locale/webroot/img/flags/tz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ua.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ug.gif create mode 100644 app/Plugin/locale/webroot/img/flags/um.gif create mode 100644 app/Plugin/locale/webroot/img/flags/us.gif create mode 100644 app/Plugin/locale/webroot/img/flags/uy.gif create mode 100644 app/Plugin/locale/webroot/img/flags/uz.gif create mode 100644 app/Plugin/locale/webroot/img/flags/va.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vc.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ve.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vg.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vi.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vn.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vs.gif create mode 100644 app/Plugin/locale/webroot/img/flags/vu.gif create mode 100644 app/Plugin/locale/webroot/img/flags/wales.gif create mode 100644 app/Plugin/locale/webroot/img/flags/wf.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ws.gif create mode 100644 app/Plugin/locale/webroot/img/flags/ye.gif create mode 100644 app/Plugin/locale/webroot/img/flags/yt.gif create mode 100644 app/Plugin/locale/webroot/img/flags/za.gif create mode 100644 app/Plugin/locale/webroot/img/flags/zm.gif create mode 100644 app/Plugin/locale/webroot/img/flags/zw.gif create mode 100644 app/Plugin/locale/webroot/js/empty create mode 100644 app/Plugin/menu/Config/bootstrap.php create mode 100644 app/Plugin/menu/Config/routes.php create mode 100644 app/Plugin/menu/Controller/Component/empty create mode 100644 app/Plugin/menu/Controller/ManageController.php create mode 100644 app/Plugin/menu/Controller/MenuAppController.php create mode 100644 app/Plugin/menu/Controller/MenuController.php create mode 100644 app/Plugin/menu/Lib/empty create mode 100644 app/Plugin/menu/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/menu/Model/Behavior/empty create mode 100644 app/Plugin/menu/Model/Menu.php create mode 100644 app/Plugin/menu/Model/MenuAppModel.php create mode 100644 app/Plugin/menu/Model/MenuLink.php create mode 100644 app/Plugin/menu/View/Elements/help.ctp create mode 100644 app/Plugin/menu/View/Elements/menu_edit.ctp create mode 100644 app/Plugin/menu/View/Elements/menu_link_node.ctp create mode 100644 app/Plugin/menu/View/Elements/toolbar.ctp create mode 100644 app/Plugin/menu/View/Helper/MenuHookHelper.php create mode 100644 app/Plugin/menu/View/Helper/TreeHelper.php create mode 100644 app/Plugin/menu/View/Manage/admin_add.ctp create mode 100644 app/Plugin/menu/View/Manage/admin_add_link.ctp create mode 100644 app/Plugin/menu/View/Manage/admin_edit.ctp create mode 100644 app/Plugin/menu/View/Manage/admin_edit_link.ctp create mode 100644 app/Plugin/menu/View/Manage/admin_index.ctp create mode 100644 app/Plugin/menu/View/Manage/admin_links.ctp create mode 100644 app/Plugin/menu/menu.yaml create mode 100644 app/Plugin/menu/webroot/css/empty create mode 100644 app/Plugin/menu/webroot/img/empty create mode 100644 app/Plugin/menu/webroot/js/nestedSortable/jquery-1.5.2.min.js create mode 100644 app/Plugin/menu/webroot/js/nestedSortable/jquery-ui-1.8.11.custom.min.js create mode 100644 app/Plugin/menu/webroot/js/nestedSortable/jquery.ui.nestedSortable.js create mode 100644 app/Plugin/node/Config/bootstrap.php create mode 100644 app/Plugin/node/Config/routes.php create mode 100644 app/Plugin/node/Controller/Component/NodeHookComponent.php create mode 100644 app/Plugin/node/Controller/ContentsController.php create mode 100644 app/Plugin/node/Controller/NodeAppController.php create mode 100644 app/Plugin/node/Controller/NodeController.php create mode 100644 app/Plugin/node/Controller/TypesController.php create mode 100644 app/Plugin/node/Lib/empty create mode 100644 app/Plugin/node/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/node/Model/Behavior/NodeHookBehavior.php create mode 100644 app/Plugin/node/Model/Node.php create mode 100644 app/Plugin/node/Model/NodeAppModel.php create mode 100644 app/Plugin/node/Model/NodeType.php create mode 100644 app/Plugin/node/View/Contents/admin_add.ctp create mode 100644 app/Plugin/node/View/Contents/admin_create.ctp create mode 100644 app/Plugin/node/View/Contents/admin_edit.ctp create mode 100644 app/Plugin/node/View/Contents/admin_index.ctp create mode 100644 app/Plugin/node/View/Elements/help.ctp create mode 100644 app/Plugin/node/View/Elements/node/comments.ctp create mode 100644 app/Plugin/node/View/Elements/node/comments_form.ctp create mode 100644 app/Plugin/node/View/Elements/node/edit.ctp create mode 100644 app/Plugin/node/View/Elements/node/front_page.ctp create mode 100644 app/Plugin/node/View/Elements/node/node.ctp create mode 100644 app/Plugin/node/View/Elements/search_block.ctp create mode 100644 app/Plugin/node/View/Elements/search_form.ctp create mode 100644 app/Plugin/node/View/Elements/toolbar-display.ctp create mode 100644 app/Plugin/node/View/Elements/toolbar-index.ctp create mode 100644 app/Plugin/node/View/Elements/toolbar-types.ctp create mode 100644 app/Plugin/node/View/Helper/NodeHookHelper.php create mode 100644 app/Plugin/node/View/Node/details.ctp create mode 100644 app/Plugin/node/View/Node/index.ctp create mode 100644 app/Plugin/node/View/Node/search.ctp create mode 100644 app/Plugin/node/View/Types/admin_add.ctp create mode 100644 app/Plugin/node/View/Types/admin_display.ctp create mode 100644 app/Plugin/node/View/Types/admin_edit.ctp create mode 100644 app/Plugin/node/View/Types/admin_field_formatter.ctp create mode 100644 app/Plugin/node/View/Types/admin_field_settings.ctp create mode 100644 app/Plugin/node/View/Types/admin_fields.ctp create mode 100644 app/Plugin/node/View/Types/admin_index.ctp create mode 100644 app/Plugin/node/node.yaml create mode 100644 app/Plugin/node/webroot/css/empty create mode 100644 app/Plugin/node/webroot/img/promote.png create mode 100644 app/Plugin/node/webroot/img/sticky.png create mode 100644 app/Plugin/node/webroot/js/empty create mode 100644 app/Plugin/system/Config/bootstrap.php create mode 100644 app/Plugin/system/Config/routes.php create mode 100644 app/Plugin/system/Controller/Component/SystemHookComponent.php create mode 100644 app/Plugin/system/Controller/ConfigurationController.php create mode 100644 app/Plugin/system/Controller/DashboardController.php create mode 100644 app/Plugin/system/Controller/HelpController.php create mode 100644 app/Plugin/system/Controller/ModulesController.php create mode 100644 app/Plugin/system/Controller/StructureController.php create mode 100644 app/Plugin/system/Controller/SystemAppController.php create mode 100644 app/Plugin/system/Controller/SystemController.php create mode 100644 app/Plugin/system/Controller/ThemesController.php create mode 100644 app/Plugin/system/Lib/empty create mode 100644 app/Plugin/system/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/system/Model/Behavior/empty create mode 100644 app/Plugin/system/Model/Module.php create mode 100644 app/Plugin/system/Model/SystemAppModel.php create mode 100644 app/Plugin/system/Model/Variable.php create mode 100644 app/Plugin/system/View/Configuration/admin_index.ctp create mode 100644 app/Plugin/system/View/Dashboard/admin_index.ctp create mode 100644 app/Plugin/system/View/Elements/help.ctp create mode 100644 app/Plugin/system/View/Elements/system_recent_content.ctp create mode 100644 app/Plugin/system/View/Help/admin_index.ctp create mode 100644 app/Plugin/system/View/Help/admin_module.ctp create mode 100644 app/Plugin/system/View/Helper/SystemHookHelper.php create mode 100644 app/Plugin/system/View/Modules/admin_index.ctp create mode 100644 app/Plugin/system/View/Modules/admin_settings.ctp create mode 100644 app/Plugin/system/View/Structure/admin_index.ctp create mode 100644 app/Plugin/system/View/Themes/admin_index.ctp create mode 100644 app/Plugin/system/View/Themes/admin_settings.ctp create mode 100644 app/Plugin/system/system.yaml create mode 100644 app/Plugin/system/webroot/css/empty create mode 100644 app/Plugin/system/webroot/img/empty create mode 100644 app/Plugin/system/webroot/js/empty create mode 100644 app/Plugin/taxonomy/Config/bootstrap.php create mode 100644 app/Plugin/taxonomy/Config/routes.php create mode 100644 app/Plugin/taxonomy/Controller/Component/empty create mode 100644 app/Plugin/taxonomy/Controller/TaxonomyAppController.php create mode 100644 app/Plugin/taxonomy/Controller/TaxonomyController.php create mode 100644 app/Plugin/taxonomy/Controller/VocabulariesController.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/Config/bootstrap.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/Config/routes.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/Controller/Component/empty create mode 100644 app/Plugin/taxonomy/Fields/field_terms/Model/Behavior/FieldTermsHookBehavior.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/Model/FieldTermsAppModel.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/View/Elements/edit.ctp create mode 100644 app/Plugin/taxonomy/Fields/field_terms/View/Elements/formatter_from.ctp create mode 100644 app/Plugin/taxonomy/Fields/field_terms/View/Elements/settings_from.ctp create mode 100644 app/Plugin/taxonomy/Fields/field_terms/View/Elements/view.ctp create mode 100644 app/Plugin/taxonomy/Fields/field_terms/View/Helper/FieldTermsHookHelper.php create mode 100644 app/Plugin/taxonomy/Fields/field_terms/field_terms.yaml create mode 100644 app/Plugin/taxonomy/Lib/empty create mode 100644 app/Plugin/taxonomy/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/taxonomy/Model/Behavior/empty create mode 100644 app/Plugin/taxonomy/Model/TaxonomyAppModel.php create mode 100644 app/Plugin/taxonomy/Model/Term.php create mode 100644 app/Plugin/taxonomy/Model/Vocabulary.php create mode 100644 app/Plugin/taxonomy/View/Elements/help.ctp create mode 100644 app/Plugin/taxonomy/View/Elements/taxonomy_vocabularies_settings.ctp create mode 100644 app/Plugin/taxonomy/View/Elements/term_node.ctp create mode 100644 app/Plugin/taxonomy/View/Elements/toolbar.ctp create mode 100644 app/Plugin/taxonomy/View/Helper/TaxonomyHookHelper.php create mode 100644 app/Plugin/taxonomy/View/Vocabularies/admin_add.ctp create mode 100644 app/Plugin/taxonomy/View/Vocabularies/admin_edit.ctp create mode 100644 app/Plugin/taxonomy/View/Vocabularies/admin_edit_term.ctp create mode 100644 app/Plugin/taxonomy/View/Vocabularies/admin_index.ctp create mode 100644 app/Plugin/taxonomy/View/Vocabularies/admin_terms.ctp create mode 100644 app/Plugin/taxonomy/taxonomy.yaml create mode 100644 app/Plugin/taxonomy/webroot/css/empty create mode 100644 app/Plugin/taxonomy/webroot/img/empty create mode 100644 app/Plugin/taxonomy/webroot/js/empty create mode 100644 app/Plugin/user/Config/bootstrap.php create mode 100644 app/Plugin/user/Config/routes.php create mode 100644 app/Plugin/user/Controller/Component/MailerComponent.php create mode 100644 app/Plugin/user/Controller/Component/UserHookComponent.php create mode 100644 app/Plugin/user/Controller/DisplayController.php create mode 100644 app/Plugin/user/Controller/FieldsController.php create mode 100644 app/Plugin/user/Controller/ListController.php create mode 100644 app/Plugin/user/Controller/PermissionsController.php create mode 100644 app/Plugin/user/Controller/RolesController.php create mode 100644 app/Plugin/user/Controller/UserAppController.php create mode 100644 app/Plugin/user/Controller/UserController.php create mode 100644 app/Plugin/user/Lib/empty create mode 100644 app/Plugin/user/Locale/eng/LC_MESSAGES/core.po create mode 100644 app/Plugin/user/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/Plugin/user/Model/Behavior/UserHookBehavior.php create mode 100644 app/Plugin/user/Model/Role.php create mode 100644 app/Plugin/user/Model/User.php create mode 100644 app/Plugin/user/Model/UserAppModel.php create mode 100644 app/Plugin/user/View/Dashboard/index.ctp create mode 100644 app/Plugin/user/View/Dashboard/login.ctp create mode 100644 app/Plugin/user/View/Display/admin_field_formatter.ctp create mode 100644 app/Plugin/user/View/Display/admin_index.ctp create mode 100644 app/Plugin/user/View/Elements/help.ctp create mode 100644 app/Plugin/user/View/Elements/permission-node.ctp create mode 100644 app/Plugin/user/View/Elements/settings.ctp create mode 100644 app/Plugin/user/View/Elements/toolbar.ctp create mode 100644 app/Plugin/user/View/Elements/user_login_block.ctp create mode 100644 app/Plugin/user/View/Elements/user_new_block.ctp create mode 100644 app/Plugin/user/View/Elements/user_new_block_settings.ctp create mode 100644 app/Plugin/user/View/Fields/admin_field_settings.ctp create mode 100644 app/Plugin/user/View/Fields/admin_index.ctp create mode 100644 app/Plugin/user/View/Helper/TreeHelper.php create mode 100644 app/Plugin/user/View/Helper/UserHookHelper.php create mode 100644 app/Plugin/user/View/List/admin_add.ctp create mode 100644 app/Plugin/user/View/List/admin_edit.ctp create mode 100644 app/Plugin/user/View/List/admin_index.ctp create mode 100644 app/Plugin/user/View/Permissions/admin_edit.ctp create mode 100644 app/Plugin/user/View/Permissions/admin_index.ctp create mode 100644 app/Plugin/user/View/Permissions/admin_toggle.ctp create mode 100644 app/Plugin/user/View/Roles/admin_edit.ctp create mode 100644 app/Plugin/user/View/Roles/admin_index.ctp create mode 100644 app/Plugin/user/View/User/admin_login.ctp create mode 100644 app/Plugin/user/View/User/login.ctp create mode 100644 app/Plugin/user/View/User/my_account.ctp create mode 100644 app/Plugin/user/View/User/password_recovery.ctp create mode 100644 app/Plugin/user/View/User/profile.ctp create mode 100644 app/Plugin/user/View/User/register.ctp create mode 100644 app/Plugin/user/user.yaml create mode 100644 app/Plugin/user/webroot/css/treeview.css create mode 100644 app/Plugin/user/webroot/img/allow-0.gif create mode 100644 app/Plugin/user/webroot/img/allow-1.gif create mode 100644 app/Plugin/user/webroot/img/icon_padlock.gif create mode 100644 app/Plugin/user/webroot/img/key.png create mode 100644 app/Plugin/user/webroot/img/treeview-default-line.gif create mode 100644 app/Plugin/user/webroot/img/treeview-default.gif create mode 100644 app/Plugin/user/webroot/js/acos.js create mode 100644 app/Plugin/user/webroot/js/jquery.cookie.js create mode 100644 app/Plugin/user/webroot/js/treeview.js create mode 100644 app/Vendor/PclZip.php create mode 100644 app/Vendor/Spyc.php create mode 100644 app/Vendor/Upload.php create mode 100644 app/Vendor/shells/tasks/empty create mode 100644 app/Vendor/shells/templates/empty create mode 100644 app/View/Elements/default_renderNode.ctp create mode 100644 app/View/Elements/default_theme_block.ctp create mode 100644 app/View/Elements/default_theme_breadcrumb.ctp create mode 100644 app/View/Emails/html/empty create mode 100644 app/View/Emails/text/empty create mode 100644 app/View/Errors/error400.ctp create mode 100644 app/View/Errors/error500.ctp create mode 100644 app/View/Errors/private_action.ctp create mode 100644 app/View/Helper/AppHelper.php create mode 100644 app/View/Helper/CustomHooksHelper.php create mode 100644 app/View/Helper/LayoutHelper.php create mode 100644 app/View/Helper/MenuHelper.php create mode 100644 app/View/Helper/QaFormHelper.php create mode 100644 app/View/Helper/QaHtmlHelper.php create mode 100644 app/View/Helper/TableHelper.php create mode 100644 app/View/Install/database.ctp create mode 100644 app/View/Install/finish.ctp create mode 100644 app/View/Install/license.ctp create mode 100644 app/View/Install/server_test.ctp create mode 100644 app/View/Install/user_account.ctp create mode 100644 app/View/Layouts/emails/html/empty create mode 100644 app/View/Layouts/emails/text/empty create mode 100644 app/View/Layouts/error.ctp create mode 100644 app/View/Layouts/install.ctp create mode 100644 app/View/Layouts/js/empty create mode 100644 app/View/Layouts/rss.ctp create mode 100644 app/View/Layouts/rss/empty create mode 100644 app/View/Layouts/xml/empty create mode 100644 app/View/Scaffolds/empty create mode 100644 app/View/Themed/AdminDefault/AdminDefault.yaml create mode 100644 app/View/Themed/AdminDefault/Layouts/default.ctp create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Config/bootstrap.php create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Config/routes.php create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Controller/Component/empty create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Controller/SettingsController.php create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Locale/spa/LC_MESSAGES/core.po create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/Model/Behavior/empty create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/View/Elements/content-menu.ctp create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/View/Elements/settings_form.ctp create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/View/Helper/ThemeAdminDefaultHookHelper.php create mode 100644 app/View/Themed/AdminDefault/Plugin/theme_admin_default/View/settings/admin_index.ctp create mode 100644 app/View/Themed/AdminDefault/thumbnail.png create mode 100644 app/View/Themed/AdminDefault/webroot/css/reset.css create mode 100644 app/View/Themed/AdminDefault/webroot/css/styles.css create mode 100644 app/View/Themed/AdminDefault/webroot/files/empty create mode 100644 app/View/Themed/AdminDefault/webroot/img/boxes/alert.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/boxes/bubble.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/boxes/error.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/boxes/success.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/down.gif create mode 100644 app/View/Themed/AdminDefault/webroot/img/footer-top-bg.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/header-bottom-bg.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/menu-active-arrow.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/menu-nav-white.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/menu-nav.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/top-shadow.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/topmenu_arrow.png create mode 100644 app/View/Themed/AdminDefault/webroot/img/up.gif create mode 100644 app/View/Themed/AdminDefault/webroot/js/empty create mode 100644 app/View/Themed/Default/Default.yaml create mode 100644 app/View/Themed/Default/Layouts/default.ctp create mode 100644 app/View/Themed/Default/Plugin/theme_default/Config/bootstrap.php create mode 100644 app/View/Themed/Default/Plugin/theme_default/Config/routes.php create mode 100644 app/View/Themed/Default/Plugin/theme_default/Controller/Component/empty create mode 100644 app/View/Themed/Default/Plugin/theme_default/Controller/SettingsController.php create mode 100644 app/View/Themed/Default/Plugin/theme_default/Model/Behavior/empty create mode 100644 app/View/Themed/Default/Plugin/theme_default/View/Elements/settings_form.ctp create mode 100644 app/View/Themed/Default/Plugin/theme_default/View/Elements/theme_default_slider.ctp create mode 100644 app/View/Themed/Default/Plugin/theme_default/View/Elements/theme_default_slider_settings.ctp create mode 100644 app/View/Themed/Default/Plugin/theme_default/View/Helper/ThemeDefaultHookHelper.php create mode 100644 app/View/Themed/Default/thumbnail.png create mode 100644 app/View/Themed/Default/webroot/css/nivo-slider.css create mode 100644 app/View/Themed/Default/webroot/css/reset.css create mode 100644 app/View/Themed/Default/webroot/css/styles.css create mode 100644 app/View/Themed/Default/webroot/files/empty create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-blue.png create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-green.png create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-orange.png create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-purple.png create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-red.png create mode 100644 app/View/Themed/Default/webroot/img/big-buttons/big-button-turquoise.png create mode 100644 app/View/Themed/Default/webroot/img/boxes/alert.png create mode 100644 app/View/Themed/Default/webroot/img/boxes/bubble.png create mode 100644 app/View/Themed/Default/webroot/img/boxes/error.png create mode 100644 app/View/Themed/Default/webroot/img/boxes/success.png create mode 100644 app/View/Themed/Default/webroot/img/bubble.png create mode 100644 app/View/Themed/Default/webroot/img/comment-quote.gif create mode 100644 app/View/Themed/Default/webroot/img/content-bg.png create mode 100644 app/View/Themed/Default/webroot/img/feed.png create mode 100644 app/View/Themed/Default/webroot/img/footer-top-bg.png create mode 100644 app/View/Themed/Default/webroot/img/gear.png create mode 100644 app/View/Themed/Default/webroot/img/header-bottom-bg.png create mode 100644 app/View/Themed/Default/webroot/img/menu-nav.png create mode 100644 app/View/Themed/Default/webroot/img/pen.png create mode 100644 app/View/Themed/Default/webroot/img/quote-bg.png create mode 100644 app/View/Themed/Default/webroot/img/quote-icon.png create mode 100644 app/View/Themed/Default/webroot/img/quote-inner-bg.png create mode 100644 app/View/Themed/Default/webroot/img/slider/slider-arrows.png create mode 100644 app/View/Themed/Default/webroot/img/slider/slider-bullets.png create mode 100644 app/View/Themed/Default/webroot/img/slider/slider-loading.gif create mode 100644 app/View/Themed/Default/webroot/img/slider/slider-shadow.jpg create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-black.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-blue.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-green.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-lightblue.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-orange.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-pink.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-purple.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-red.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-silver.png create mode 100644 app/View/Themed/Default/webroot/img/small-buttons/small-button-teal.png create mode 100644 app/View/Themed/Default/webroot/img/top-shadow.png create mode 100644 app/View/Themed/Default/webroot/js/Colaborate-Thin_400.font.js create mode 100644 app/View/Themed/Default/webroot/js/cufon-yui.js create mode 100644 app/View/Themed/Default/webroot/js/farbtastic.js create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/LICENSE.txt create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/farbtastic.css create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/farbtastic.js create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/marker.png create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/mask.png create mode 100644 app/View/Themed/Default/webroot/js/farbtastic/wheel.png create mode 100644 app/View/Themed/Default/webroot/js/nivo-slider/jquery.nivo.slider.pack.js create mode 100644 app/View/Themed/Default/webroot/js/nivo-slider/license.txt create mode 100644 app/index.php create mode 100644 app/tmp/cache/i18n/empty create mode 100644 app/tmp/cache/installer/empty create mode 100644 app/tmp/cache/models/empty create mode 100644 app/tmp/cache/persistent/empty create mode 100644 app/tmp/cache/views/empty create mode 100644 app/tmp/logs/empty create mode 100644 app/tmp/sessions/empty create mode 100644 app/webroot/.htaccess create mode 100644 app/webroot/css/error.css create mode 100644 app/webroot/css/install.css create mode 100644 app/webroot/css/reset.css create mode 100644 app/webroot/favicon.ico create mode 100644 app/webroot/files/slider/1_eng.jpg create mode 100644 app/webroot/files/slider/1_spa.jpg create mode 100644 app/webroot/files/slider/2_eng.jpg create mode 100644 app/webroot/files/slider/2_spa.jpg create mode 100644 app/webroot/files/slider/3_eng.jpg create mode 100644 app/webroot/files/slider/3_spa.jpg create mode 100644 app/webroot/img/accept.png create mode 100644 app/webroot/img/anonymous_avatar.jpg create mode 100644 app/webroot/img/boxes/alert.png create mode 100644 app/webroot/img/boxes/bubble.png create mode 100644 app/webroot/img/boxes/error.png create mode 100644 app/webroot/img/boxes/success.png create mode 100644 app/webroot/img/error.png create mode 100644 app/webroot/img/logo.png create mode 100644 app/webroot/index.php create mode 100644 app/webroot/js/jquery.js create mode 100644 app/webroot/js/json.js create mode 100644 app/webroot/js/quickapps.js create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-icons_222222_256x240.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-icons_228ef1_256x240.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-icons_ef8c08_256x240.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-icons_ffd27a_256x240.png create mode 100644 app/webroot/js/ui/css/ui-lightness/images/ui-icons_ffffff_256x240.png create mode 100644 app/webroot/js/ui/css/ui-lightness/styles.css create mode 100644 app/webroot/js/ui/jquery-ui.js create mode 100644 app/webroot/js/vendors.php create mode 100644 index.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..f23dbaf6 --- /dev/null +++ b/.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..dcfa4c23 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Modules/empty b/Modules/empty new file mode 100644 index 00000000..e69de29b diff --git a/README.mdown b/README.mdown new file mode 100644 index 00000000..00c961cd --- /dev/null +++ b/README.mdown @@ -0,0 +1,33 @@ +# QuickApps + +**QuickApps** is a free open source, content management system for PHP, released under GPL License and powered by [CakePHP 2.0](http://cakephp.org) MVC framework. + +## Requirements + + * Apache with mod_rewrite + * PHP 5.2.6 or higher + * PHP safe mode disabled + * MySQL 4.1 or higher + +## Installation + +#### Github based installer (Recomended) + + * Download from github the latest version as zip. + * Extract the archive. Upload the content to your server. + * visit http://your-site.com/ from your browser and follow the instructions. + +#### Quick-Installer + + * Download the one-file installer from [here](http://cms.quickapps.es/files/installer/index.install) + * Rename from 'index.install' to 'index.php' + * Upload the new 'index.php' to your server + * visit http://your-site.com/ from your browser and follow the instructions. + +#### Manual installation + + * Create a new MySQL database ('utf8_unicode_ci' collation) + * Use the SQL dump file app/Config/Schema/manual/quickapps.install.sql + * Rename app/Config/database.php.install to database.php and set your database information (use 'qa_' as prefix). + * Copy the file app/Config/install (no extension) to app/Config + * The default username and password are: **admin/admin** \ No newline at end of file diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 00000000..0ed8662e --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + \ No newline at end of file diff --git a/app/Cake/Cache/Cache.php b/app/Cake/Cache/Cache.php new file mode 100644 index 00000000..f8c2d4d6 --- /dev/null +++ b/app/Cake/Cache/Cache.php @@ -0,0 +1,600 @@ + 'Apc', + * 'prefix' => 'my_app_' + * )); + * }}} + * + * This would configure an APC cache engine to the 'shared' alias. You could then read and write + * to that cache alias by using it for the `$config` parameter in the various Cache methods. In + * general all Cache operations are supported by all cache engines. However, Cache::increment() and + * Cache::decrement() are not supported by File caching. + * + * @package Cake.Cache + */ +class Cache { + +/** + * Cache configuration stack + * Keeps the permanent/default settings for each cache engine. + * These settings are used to reset the engines after temporary modification. + * + * @var array + */ + protected static $_config = array(); + +/** + * Whether to reset the settings with the next call to Cache::set(); + * + * @var array + */ + protected static $_reset = false; + +/** + * Engine instances keyed by configuration name. + * + * @var array + */ + protected static $_engines = array(); + +/** + * Set the cache configuration to use. config() can + * both create new configurations, return the settings for already configured + * configurations. + * + * To create a new configuration, or to modify an existing configuration permanently: + * + * `Cache::config('my_config', array('engine' => 'File', 'path' => TMP));` + * + * If you need to modify a configuration temporarily, use Cache::set(). + * To get the settings for a configuration: + * + * `Cache::config('default');` + * + * There are 5 built-in caching engines: + * + * - `FileEngine` - Uses simple files to store content. Poor performance, but good for + * storing large objects, or things that are not IO sensitive. + * - `ApcEngine` - Uses the APC object cache, one of the fastest caching engines. + * - `MemcacheEngine` - Uses the PECL::Memcache extension and Memcached for storage. + * Fast reads/writes, and benefits from memcache being distributed. + * - `XcacheEngine` - Uses the Xcache extension, an alternative to APC. + * - `WincacheEngine` - Uses Windows Cache Extension for PHP. Supports wincache 1.1.0 and higher. + * + * The following keys are used in core cache engines: + * + * - `duration` Specify how long items in this cache configuration last. + * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace + * with either another cache config or annother application. + * - `probability` Probability of hitting a cache gc cleanup. Setting to 0 will disable + * cache::gc from ever being called automatically. + * - `servers' Used by memcache. Give the address of the memcached servers to use. + * - `compress` Used by memcache. Enables memcache's compressed format. + * - `serialize` Used by FileCache. Should cache objects be serialized first. + * - `path` Used by FileCache. Path to where cachefiles should be saved. + * - `lock` Used by FileCache. Should files be locked before writing to them? + * - `user` Used by Xcache. Username for XCache + * - `password` Used by Xcache. Password for XCache + * + * @see app/Config/core.php for configuration settings + * @param string $name Name of the configuration + * @param array $settings Optional associative array of settings passed to the engine + * @return array(engine, settings) on success, false on failure + * @throws CacheException + */ + public static function config($name = null, $settings = array()) { + if (is_array($name)) { + $settings = $name; + } + + $current = array(); + if (isset(self::$_config[$name])) { + $current = self::$_config[$name]; + } + + if (!empty($settings)) { + self::$_config[$name] = array_merge($current, $settings); + } + + if (empty(self::$_config[$name]['engine'])) { + return false; + } + + $engine = self::$_config[$name]['engine']; + + if (!isset(self::$_engines[$name])) { + self::_buildEngine($name); + $settings = self::$_config[$name] = self::settings($name); + } elseif ($settings = self::set(self::$_config[$name], null, $name)) { + self::$_config[$name] = $settings; + } + return compact('engine', 'settings'); + } + +/** + * Finds and builds the instance of the required engine class. + * + * @param string $name Name of the config array that needs an engine instance built + * @return boolean + * @throws CacheException + */ + protected static function _buildEngine($name) { + $config = self::$_config[$name]; + + list($plugin, $class) = pluginSplit($config['engine'], true); + $cacheClass = $class . 'Engine'; + App::uses($cacheClass, $plugin . 'Cache/Engine'); + if (!class_exists($cacheClass)) { + return false; + } + $cacheClass = $class . 'Engine'; + if (!is_subclass_of($cacheClass, 'CacheEngine')) { + throw new CacheException(__d('cake_dev', 'Cache engines must use CacheEngine as a base class.')); + } + self::$_engines[$name] = new $cacheClass(); + if (self::$_engines[$name]->init($config)) { + if (self::$_engines[$name]->settings['probability'] && time() % self::$_engines[$name]->settings['probability'] === 0) { + self::$_engines[$name]->gc(); + } + return true; + } + return false; + } + +/** + * Returns an array containing the currently configured Cache settings. + * + * @return array Array of configured Cache config names. + */ + public static function configured() { + return array_keys(self::$_config); + } + +/** + * Drops a cache engine. Deletes the cache configuration information + * If the deleted configuration is the last configuration using an certain engine, + * the Engine instance is also unset. + * + * @param string $name A currently configured cache config you wish to remove. + * @return boolean success of the removal, returns false when the config does not exist. + */ + public static function drop($name) { + if (!isset(self::$_config[$name])) { + return false; + } + unset(self::$_config[$name], self::$_engines[$name]); + return true; + } + +/** + * Temporarily change the settings on a cache config. The settings will persist for the next write + * operation (write, decrement, increment, clear). Any reads that are done before the write, will + * use the modified settings. If `$settings` is empty, the settings will be reset to the + * original configuration. + * + * Can be called with 2 or 3 parameters. To set multiple values at once. + * + * `Cache::set(array('duration' => '+30 minutes'), 'my_config');` + * + * Or to set one value. + * + * `Cache::set('duration', '+30 minutes', 'my_config');` + * + * To reset a config back to the originally configured values. + * + * `Cache::set(null, 'my_config');` + * + * @param mixed $settings Optional string for simple name-value pair or array + * @param string $value Optional for a simple name-value pair + * @param string $config The configuration name you are changing. Defaults to 'default' + * @return array Array of settings. + */ + public static function set($settings = array(), $value = null, $config = 'default') { + if (is_array($settings) && $value !== null) { + $config = $value; + } + if (!isset(self::$_config[$config]) || !isset(self::$_engines[$config])) { + return false; + } + if (!empty($settings)) { + self::$_reset = true; + } + + if (self::$_reset === true) { + if (empty($settings)) { + self::$_reset = false; + $settings = self::$_config[$config]; + } else { + if (is_string($settings) && $value !== null) { + $settings = array($settings => $value); + } + $settings = array_merge(self::$_config[$config], $settings); + if (isset($settings['duration']) && !is_numeric($settings['duration'])) { + $settings['duration'] = strtotime($settings['duration']) - time(); + } + } + self::$_engines[$config]->settings = $settings; + } + return self::settings($config); + } + +/** + * Garbage collection + * + * Permanently remove all expired and deleted data + * + * @param string $config The config name you wish to have garbage collected. Defaults to 'default' + * @return void + */ + public static function gc($config = 'default') { + self::$_engines[$config]->gc(); + } + +/** + * Write data for key into cache. Will automatically use the currently + * active cache configuration. To set the currently active configuration use + * Cache::config() + * + * ### Usage: + * + * Writing to the active cache config: + * + * `Cache::write('cached_data', $data);` + * + * Writing to a specific cache config: + * + * `Cache::write('cached_data', $data, 'long_term');` + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached - anything except a resource + * @param string $config Optional string configuration name to write to. Defaults to 'default' + * @return boolean True if the data was successfully cached, false on failure + */ + public static function write($key, $value, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return null; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + + if (!$key || is_resource($value)) { + return false; + } + + $success = self::$_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']); + self::set(null, $config); + if ($success === false && $value !== '') { + trigger_error( + __d('cake_dev', + "%s cache was unable to write '%s' to %s cache", + $config, + $key, + self::$_engines[$config]->settings['engine'] + ), + E_USER_WARNING + ); + } + return $success; + } + +/** + * Read a key from the cache. Will automatically use the currently + * active cache configuration. To set the currently active configuration use + * Cache::config() + * + * ### Usage: + * + * Reading from the active cache configuration. + * + * `Cache::read('my_data');` + * + * Reading from a specific cache configuration. + * + * `Cache::read('my_data', 'long_term');` + * + * @param string $key Identifier for the data + * @param string $config optional name of the configuration to use. Defaults to 'default' + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + public static function read($key, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return null; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + if (!$key) { + return false; + } + return self::$_engines[$config]->read($settings['prefix'] . $key); + } + +/** + * Increment a number under the key and return incremented value. + * + * @param string $key Identifier for the data + * @param integer $offset How much to add + * @param string $config Optional string configuration name. Defaults to 'default' + * @return mixed new value, or false if the data doesn't exist, is not integer, + * or if there was an error fetching it. + */ + public static function increment($key, $offset = 1, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return null; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + + if (!$key || !is_integer($offset) || $offset < 0) { + return false; + } + $success = self::$_engines[$config]->increment($settings['prefix'] . $key, $offset); + self::set(null, $config); + return $success; + } +/** + * Decrement a number under the key and return decremented value. + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @param string $config Optional string configuration name. Defaults to 'default' + * @return mixed new value, or false if the data doesn't exist, is not integer, + * or if there was an error fetching it + */ + public static function decrement($key, $offset = 1, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return null; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + + if (!$key || !is_integer($offset) || $offset < 0) { + return false; + } + $success = self::$_engines[$config]->decrement($settings['prefix'] . $key, $offset); + self::set(null, $config); + return $success; + } +/** + * Delete a key from the cache. + * + * ### Usage: + * + * Deleting from the active cache configuration. + * + * `Cache::delete('my_data');` + * + * Deleting from a specific cache configuration. + * + * `Cache::delete('my_data', 'long_term');` + * + * @param string $key Identifier for the data + * @param string $config name of the configuration to use. Defaults to 'default' + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public static function delete($key, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return null; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + if (!$key) { + return false; + } + + $success = self::$_engines[$config]->delete($settings['prefix'] . $key); + self::set(null, $config); + return $success; + } + +/** + * Delete all keys from the cache. + * + * @param boolean $check if true will check expiration, otherwise delete all + * @param string $config name of the configuration to use. Defaults to 'default' + * @return boolean True if the cache was successfully cleared, false otherwise + */ + public static function clear($check = false, $config = 'default') { + if (!self::isInitialized($config)) { + return false; + } + $success = self::$_engines[$config]->clear($check); + self::set(null, $config); + return $success; + } + +/** + * Check if Cache has initialized a working config for the given name. + * + * @param string $config name of the configuration to use. Defaults to 'default' + * @return boolean Whether or not the config name has been initialized. + */ + public static function isInitialized($config = 'default') { + if (Configure::read('Cache.disable')) { + return false; + } + return isset(self::$_engines[$config]); + } + +/** + * Return the settings for the named cache engine. + * + * @param string $name Name of the configuration to get settings for. Defaults to 'default' + * @return array list of settings for this engine + * @see Cache::config() + */ + public static function settings($name = 'default') { + if (!empty(self::$_engines[$name])) { + return self::$_engines[$name]->settings(); + } + return array(); + } +} + +/** + * Storage engine for CakePHP caching + * + * @package Cake.Cache + */ +abstract class CacheEngine { + +/** + * Settings of current engine instance + * + * @var array + */ + public $settings = array(); + +/** + * Initialize the cache engine + * + * Called automatically by the cache frontend + * + * @param array $settings Associative array of parameters for the engine + * @return boolean True if the engine has been successfully initialized, false if not + */ + public function init($settings = array()) { + $this->settings = array_merge( + array('prefix' => 'cake_', 'duration'=> 3600, 'probability'=> 100), + $this->settings, + $settings + ); + if (!is_numeric($this->settings['duration'])) { + $this->settings['duration'] = strtotime($this->settings['duration']) - time(); + } + return true; + } + +/** + * Garbage collection + * + * Permanently remove all expired and deleted data + * @return void + */ + public function gc() { } + +/** + * Write value for a key into cache + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param mixed $duration How long to cache for. + * @return boolean True if the data was successfully cached, false on failure + */ + abstract public function write($key, $value, $duration); + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + abstract public function read($key); + +/** + * Increment a number under the key and return incremented value + * + * @param string $key Identifier for the data + * @param integer $offset How much to add + * @return New incremented value, false otherwise + */ + abstract public function increment($key, $offset = 1); + +/** + * Decrement a number under the key and return decremented value + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @return New incremented value, false otherwise + */ + abstract public function decrement($key, $offset = 1); + +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + abstract public function delete($key); + +/** + * Delete all keys from the cache + * + * @param boolean $check if true will check expiration, otherwise delete all + * @return boolean True if the cache was successfully cleared, false otherwise + */ + abstract public function clear($check); + +/** + * Cache Engine settings + * + * @return array settings + */ + public function settings() { + return $this->settings; + } + +/** + * Generates a safe key for use with cache engine storage engines. + * + * @param string $key the key passed over + * @return mixed string $key or false + */ + public function key($key) { + if (empty($key)) { + return false; + } + $key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key))); + return $key; + } +} diff --git a/app/Cake/Cache/Engine/ApcEngine.php b/app/Cake/Cache/Engine/ApcEngine.php new file mode 100644 index 00000000..37cfde20 --- /dev/null +++ b/app/Cake/Cache/Engine/ApcEngine.php @@ -0,0 +1,130 @@ + 'Apc', 'prefix' => Inflector::slug(APP_DIR) . '_'), $settings)); + return function_exists('apc_cache_info'); + } + +/** + * Write data for key into cache + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param integer $duration How long to cache the data, in seconds + * @return boolean True if the data was successfully cached, false on failure + */ + public function write($key, $value, $duration) { + if ($duration == 0) { + $expires = 0; + } else { + $expires = time() + $duration; + } + apc_store($key.'_expires', $expires, $duration); + return apc_store($key, $value, $duration); + } + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + public function read($key) { + $time = time(); + $cachetime = intval(apc_fetch($key.'_expires')); + if ($cachetime !== 0 && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { + return false; + } + return apc_fetch($key); + } + +/** + * Increments the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to increment + * @return New incremented value, false otherwise + */ + public function increment($key, $offset = 1) { + return apc_inc($key, $offset); + } + +/** + * Decrements the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @return New decremented value, false otherwise + */ + public function decrement($key, $offset = 1) { + return apc_dec($key, $offset); + } + +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) { + return apc_delete($key); + } + +/** + * Delete all keys from the cache. This will clear every cache config using APC. + * + * @param boolean $check If true, nothing will be cleared, as entries are removed + * from APC as they expired. This flag is really only used by FileEngine. + * @return boolean True Returns true. + */ + public function clear($check) { + if ($check) { + return true; + } + $info = apc_cache_info('user'); + $cacheKeys = $info['cache_list']; + unset($info); + foreach ($cacheKeys as $key) { + if (strpos($key['info'], $this->settings['prefix']) === 0) { + apc_delete($key['info']); + } + } + return true; + } + +} diff --git a/app/Cake/Cache/Engine/FileEngine.php b/app/Cake/Cache/Engine/FileEngine.php new file mode 100644 index 00000000..f81889dc --- /dev/null +++ b/app/Cake/Cache/Engine/FileEngine.php @@ -0,0 +1,311 @@ + CACHE + * - prefix = string prefix for filename, default => cake_ + * - lock = enable file locking on write, default => false + * - serialize = serialize the data, default => true + * + * @var array + * @see CacheEngine::__defaults + */ + public $settings = array(); + +/** + * True unless FileEngine::__active(); fails + * + * @var boolean + */ + protected $_init = true; + +/** + * Initialize the Cache Engine + * + * Called automatically by the cache frontend + * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); + * + * @param array $settings array of setting for the engine + * @return boolean True if the engine has been successfully initialized, false if not + */ + public function init($settings = array()) { + parent::init(array_merge( + array( + 'engine' => 'File', 'path' => CACHE, 'prefix'=> 'cake_', 'lock'=> false, + 'serialize'=> true, 'isWindows' => false + ), + $settings + )); + + if (DS === '\\') { + $this->settings['isWindows'] = true; + } + if (substr($this->settings['path'], -1) !== DS) { + $this->settings['path'] .= DS; + } + return $this->_active(); + } + +/** + * Garbage collection. Permanently remove all expired and deleted data + * + * @return boolean True if garbage collection was succesful, false on failure + */ + public function gc() { + return $this->clear(true); + } + +/** + * Write data for key into cache + * + * @param string $key Identifier for the data + * @param mixed $data Data to be cached + * @param mixed $duration How long to cache the data, in seconds + * @return boolean True if the data was successfully cached, false on failure + */ + public function write($key, $data, $duration) { + if ($data === '' || !$this->_init) { + return false; + } + + if ($this->_setKey($key, true) === false) { + return false; + } + + $lineBreak = "\n"; + + if ($this->settings['isWindows']) { + $lineBreak = "\r\n"; + } + + if (!empty($this->settings['serialize'])) { + if ($this->settings['isWindows']) { + $data = str_replace('\\', '\\\\\\\\', serialize($data)); + } else { + $data = serialize($data); + } + } + + $expires = time() + $duration; + $contents = $expires . $lineBreak . $data . $lineBreak; + + if (!$handle = fopen($this->_File->getPathName(), 'c')) { + return false; + } + + if ($this->settings['lock']) { + flock($handle, LOCK_EX); + } + + $success = ftruncate($handle, 0) && fwrite($handle, $contents) && fflush($handle); + + if ($this->settings['lock']) { + flock($handle, LOCK_UN); + } + + fclose($handle); + return $success; + } + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + public function read($key) { + if (!$this->_init || $this->_setKey($key) === false) { + return false; + } + + if ($this->settings['lock']) { + $this->_File->flock(LOCK_SH); + } + + $this->_File->rewind(); + $time = time(); + $cachetime = intval($this->_File->current()); + + if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { + return false; + } + + $data = ''; + $this->_File->next(); + while ($this->_File->valid()) { + $data .= $this->_File->current(); + $this->_File->next(); + } + + if ($this->settings['lock']) { + $this->_File->flock(LOCK_UN); + } + + $data = trim($data); + + if ($data !== '' && !empty($this->settings['serialize'])) { + if ($this->settings['isWindows']) { + $data = str_replace('\\\\\\\\', '\\', $data); + } + $data = unserialize((string)$data); + } + return $data; + } + +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) { + if ($this->_setKey($key) === false || !$this->_init) { + return false; + } + $path = $this->_File->getRealPath(); + $this->_File = null; + return unlink($path); + } + +/** + * Delete all values from the cache + * + * @param boolean $check Optional - only delete expired cache items + * @return boolean True if the cache was successfully cleared, false otherwise + */ + public function clear($check) { + if (!$this->_init) { + return false; + } + $dir = dir($this->settings['path']); + if ($check) { + $now = time(); + $threshold = $now - $this->settings['duration']; + } + $prefixLength = strlen($this->settings['prefix']); + while (($entry = $dir->read()) !== false) { + if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) { + continue; + } + if ($this->_setKey($entry) === false) { + continue; + } + if ($check) { + $mtime = $this->_File->getMTime(); + + if ($mtime > $threshold) { + continue; + } + + $expires = (int)$this->_File->current(); + + if ($expires > $now) { + continue; + } + } + $path = $this->_File->getRealPath(); + $this->_File = null; + if (file_exists($path)) { + unlink($path); + } + } + $dir->close(); + return true; + } + +/** + * Not implemented + * + * @param string $key + * @param integer $offset + * @return void + * @throws CacheException + */ + public function decrement($key, $offset = 1) { + throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.')); + } + +/** + * Not implemented + * + * @param string $key + * @param integer $offset + * @return void + * @throws CacheException + */ + public function increment($key, $offset = 1) { + throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.')); + } + +/** + * Sets the current cache key this class is managing + * + * @param string $key The key + * @param boolean $createKey Whether the key should be created if it doesn't exists, or not + * @return boolean true if the cache key could be set, false otherwise + */ + protected function _setKey($key, $createKey = false) { + $path = new SplFileInfo($this->settings['path'] . $key); + + if (!$createKey && !$path->isFile()) { + return false; + } + $old = umask(0); + if (empty($this->_File) || $this->_File->getBaseName() !== $key) { + $this->_File = $path->openFile('a+'); + } + umask($old); + + return true; + } + +/** + * Determine is cache directory is writable + * + * @return boolean + */ + protected function _active() { + $dir = new SplFileInfo($this->settings['path']); + if ($this->_init && !($dir->isDir() && $dir->isWritable())) { + $this->_init = false; + trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING); + return false; + } + return true; + } +} diff --git a/app/Cake/Cache/Engine/MemcacheEngine.php b/app/Cake/Cache/Engine/MemcacheEngine.php new file mode 100644 index 00000000..af0bbb2a --- /dev/null +++ b/app/Cake/Cache/Engine/MemcacheEngine.php @@ -0,0 +1,234 @@ + 127.0.0.1. If an + * array MemcacheEngine will use them as a pool. + * - compress = boolean, default => false + * + * @var array + */ + public $settings = array(); + +/** + * Initialize the Cache Engine + * + * Called automatically by the cache frontend + * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); + * + * @param array $settings array of setting for the engine + * @return boolean True if the engine has been successfully initialized, false if not + */ + public function init($settings = array()) { + if (!class_exists('Memcache')) { + return false; + } + parent::init(array_merge(array( + 'engine'=> 'Memcache', + 'prefix' => Inflector::slug(APP_DIR) . '_', + 'servers' => array('127.0.0.1'), + 'compress'=> false, + 'persistent' => true + ), $settings) + ); + + if ($this->settings['compress']) { + $this->settings['compress'] = MEMCACHE_COMPRESSED; + } + if (!is_array($this->settings['servers'])) { + $this->settings['servers'] = array($this->settings['servers']); + } + if (!isset($this->_Memcache)) { + $return = false; + $this->_Memcache = new Memcache(); + foreach ($this->settings['servers'] as $server) { + list($host, $port) = $this->_parseServerString($server); + if ($this->_Memcache->addServer($host, $port, $this->settings['persistent'])) { + $return = true; + } + } + return $return; + } + return true; + } + +/** + * Parses the server address into the host/port. Handles both IPv6 and IPv4 + * addresses + * + * @param string $server The server address string. + * @return array Array containing host, port + */ + protected function _parseServerString($server) { + if (substr($server, 0, 1) == '[') { + $position = strpos($server, ']:'); + if ($position !== false) { + $position++; + } + } else { + $position = strpos($server, ':'); + } + $port = 11211; + $host = $server; + if ($position !== false) { + $host = substr($server, 0, $position); + $port = substr($server, $position + 1); + } + return array($host, $port); + } + +/** + * Write data for key into cache. When using memcache as your cache engine + * remember that the Memcache pecl extension does not support cache expiry times greater + * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param integer $duration How long to cache the data, in seconds + * @return boolean True if the data was successfully cached, false on failure + * @see http://php.net/manual/en/memcache.set.php + */ + public function write($key, $value, $duration) { + if ($duration > 30 * DAY) { + $duration = 0; + } + return $this->_Memcache->set($key, $value, $this->settings['compress'], $duration); + } + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + public function read($key) { + return $this->_Memcache->get($key); + } + +/** + * Increments the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to increment + * @return New incremented value, false otherwise + * @throws CacheException when you try to increment with compress = true + */ + public function increment($key, $offset = 1) { + if ($this->settings['compress']) { + throw new CacheException( + __d('cake_dev', 'Method increment() not implemented for compressed cache in %s', __CLASS__) + ); + } + return $this->_Memcache->increment($key, $offset); + } + +/** + * Decrements the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @return New decremented value, false otherwise + * @throws CacheException when you try to decrement with compress = true + */ + public function decrement($key, $offset = 1) { + if ($this->settings['compress']) { + throw new CacheException( + __d('cake_dev', 'Method decrement() not implemented for compressed cache in %s', __CLASS__) + ); + } + return $this->_Memcache->decrement($key, $offset); + } + +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) { + return $this->_Memcache->delete($key); + } + +/** + * Delete all keys from the cache + * + * @param boolean $check + * @return boolean True if the cache was successfully cleared, false otherwise + */ + public function clear($check) { + if ($check) { + return true; + } + foreach ($this->_Memcache->getExtendedStats('slabs') as $slabs) { + foreach (array_keys($slabs) as $slabId) { + if (!is_numeric($slabId)) { + continue; + } + + foreach ($this->_Memcache->getExtendedStats('cachedump', $slabId) as $stats) { + if (!is_array($stats)) { + continue; + } + foreach (array_keys($stats) as $key) { + if (strpos($key, $this->settings['prefix']) === 0) { + $this->_Memcache->delete($key); + } + } + } + } + } + return true; + } + +/** + * Connects to a server in connection pool + * + * @param string $host host ip address or name + * @param integer $port Server port + * @return boolean True if memcache server was connected + */ + public function connect($host, $port = 11211) { + if ($this->_Memcache->getServerStatus($host, $port) === 0) { + if ($this->_Memcache->connect($host, $port)) { + return true; + } + return false; + } + return true; + } +} diff --git a/app/Cake/Cache/Engine/WincacheEngine.php b/app/Cake/Cache/Engine/WincacheEngine.php new file mode 100644 index 00000000..50f27369 --- /dev/null +++ b/app/Cake/Cache/Engine/WincacheEngine.php @@ -0,0 +1,137 @@ + 'Wincache', + 'prefix' => Inflector::slug(APP_DIR) . '_'), + $settings)); + return function_exists('wincache_ucache_info'); + } + +/** + * Write data for key into cache + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param integer $duration How long to cache the data, in seconds + * @return boolean True if the data was successfully cached, false on failure + */ + public function write($key, $value, $duration) { + $expires = time() + $duration; + + $data = array( + $key . '_expires' => $expires, + $key => $value + ); + $result = wincache_ucache_set($data, null, $duration); + return empty($result); + } + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if + * there was an error fetching it + */ + public function read($key) { + $time = time(); + $cachetime = intval(wincache_ucache_get($key . '_expires')); + if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) { + return false; + } + return wincache_ucache_get($key); + } + +/** + * Increments the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to increment + * @return New incremented value, false otherwise + */ + public function increment($key, $offset = 1) { + return wincache_ucache_inc($key, $offset); + } + +/** + * Decrements the value of an integer cached key + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @return New decremented value, false otherwise + */ + public function decrement($key, $offset = 1) { + return wincache_ucache_dec($key, $offset); + } + +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) { + return wincache_ucache_delete($key); + } + +/** + * Delete all keys from the cache. This will clear every + * item in the cache matching the cache config prefix. + * + * @param boolean $check If true, nothing will be cleared, as entries will + * naturally expire in wincache.. + * @return boolean True Returns true. + */ + public function clear($check) { + if ($check) { + return true; + } + $info = wincache_ucache_info(); + $cacheKeys = $info['ucache_entries']; + unset($info); + foreach ($cacheKeys as $key) { + if (strpos($key['key_name'], $this->settings['prefix']) === 0) { + wincache_ucache_delete($key['key_name']); + } + } + return true; + } + +} diff --git a/app/Cake/Cache/Engine/XcacheEngine.php b/app/Cake/Cache/Engine/XcacheEngine.php new file mode 100644 index 00000000..d0951c52 --- /dev/null +++ b/app/Cake/Cache/Engine/XcacheEngine.php @@ -0,0 +1,175 @@ + 'Xcache', + 'prefix' => Inflector::slug(APP_DIR) . '_', + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'password' + ), $settings) + ); + return function_exists('xcache_info'); + } + +/** + * Write data for key into cache + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param integer $duration How long to cache the data, in seconds + * @return boolean True if the data was successfully cached, false on failure + */ + public function write($key, $value, $duration) { + $expires = time() + $duration; + xcache_set($key . '_expires', $expires, $duration); + return xcache_set($key, $value, $duration); + } + +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it + */ + public function read($key) { + if (xcache_isset($key)) { + $time = time(); + $cachetime = intval(xcache_get($key . '_expires')); + if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) { + return false; + } + return xcache_get($key); + } + return false; + } + +/** + * Increments the value of an integer cached key + * If the cache key is not an integer it will be treated as 0 + * + * @param string $key Identifier for the data + * @param integer $offset How much to increment + * @return New incremented value, false otherwise + */ + public function increment($key, $offset = 1) { + return xcache_inc($key, $offset); + } + +/** + * Decrements the value of an integer cached key. + * If the cache key is not an integer it will be treated as 0 + * + * @param string $key Identifier for the data + * @param integer $offset How much to subtract + * @return New decremented value, false otherwise + */ + public function decrement($key, $offset = 1) { + return xcache_dec($key, $offset); + } +/** + * Delete a key from the cache + * + * @param string $key Identifier for the data + * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + */ + public function delete($key) { + return xcache_unset($key); + } + +/** + * Delete all keys from the cache + * + * @param boolean $check + * @return boolean True if the cache was successfully cleared, false otherwise + */ + public function clear($check) { + $this->_auth(); + $max = xcache_count(XC_TYPE_VAR); + for ($i = 0; $i < $max; $i++) { + xcache_clear_cache(XC_TYPE_VAR, $i); + } + $this->_auth(true); + return true; + } + +/** + * Populates and reverses $_SERVER authentication values + * Makes necessary changes (and reverting them back) in $_SERVER + * + * This has to be done because xcache_clear_cache() needs to pass Basic Http Auth + * (see xcache.admin configuration settings) + * + * @param boolean $reverse Revert changes + * @return void + */ + protected function _auth($reverse = false) { + static $backup = array(); + $keys = array('PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password'); + foreach ($keys as $key => $setting) { + if ($reverse) { + if (isset($backup[$key])) { + $_SERVER[$key] = $backup[$key]; + unset($backup[$key]); + } else { + unset($_SERVER[$key]); + } + } else { + $value = env($key); + if (!empty($value)) { + $backup[$key] = $value; + } + if (!empty($this->settings[$setting])) { + $_SERVER[$key] = $this->settings[$setting]; + } else if (!empty($this->settings[$key])) { + $_SERVER[$key] = $this->settings[$key]; + } else { + $_SERVER[$key] = $value; + } + } + } + } +} diff --git a/app/Cake/Config/config.php b/app/Cake/Config/config.php new file mode 100644 index 00000000..adcc00e1 --- /dev/null +++ b/app/Cake/Config/config.php @@ -0,0 +1,19 @@ + $value) { + $plugins[$key] = Inflector::underscore($value); + } + $pluginPattern = implode('|', $plugins); + $match = array('plugin' => $pluginPattern); + $shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern); + + foreach ($prefixes as $prefix) { + $params = array('prefix' => $prefix, $prefix => true); + $indexParams = $params + array('action' => 'index'); + Router::connect("/{$prefix}/:plugin", $indexParams, $shortParams); + Router::connect("/{$prefix}/:plugin/:controller", $indexParams, $match); + Router::connect("/{$prefix}/:plugin/:controller/:action/*", $params, $match); + } + Router::connect('/:plugin', array('action' => 'index'), $shortParams); + Router::connect('/:plugin/:controller', array('action' => 'index'), $match); + Router::connect('/:plugin/:controller/:action/*', array(), $match); + } + + foreach ($prefixes as $prefix) { + $params = array('prefix' => $prefix, $prefix => true); + $indexParams = $params + array('action' => 'index'); + Router::connect("/{$prefix}/:controller", $indexParams); + Router::connect("/{$prefix}/:controller/:action/*", $params); + } + Router::connect('/:controller', array('action' => 'index')); + Router::connect('/:controller/:action/*'); + + $namedConfig = Router::namedConfig(); + if ($namedConfig['rules'] === false) { + Router::connectNamed(true); + } + + unset($namedConfig, $params, $indexParams, $prefix, $prefixes, $shortParams, $match, + $pluginPattern, $plugins, $key, $value); diff --git a/app/Cake/Config/unicode/casefolding/0080_00ff.php b/app/Cake/Config/unicode/casefolding/0080_00ff.php new file mode 100644 index 00000000..ac9c394a --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0080_00ff.php @@ -0,0 +1,73 @@ + 181, 'status' => 'C', 'lower' => array(956)); +$config['0080_00ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(181)); +$config['0080_00ff'][] = array('upper' => 192, 'status' => 'C', 'lower' => array(224)); /* LATIN CAPITAL LETTER A WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 193, 'status' => 'C', 'lower' => array(225)); /* LATIN CAPITAL LETTER A WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 194, 'status' => 'C', 'lower' => array(226)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 195, 'status' => 'C', 'lower' => array(227)); /* LATIN CAPITAL LETTER A WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 196, 'status' => 'C', 'lower' => array(228)); /* LATIN CAPITAL LETTER A WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 197, 'status' => 'C', 'lower' => array(229)); /* LATIN CAPITAL LETTER A WITH RING ABOVE */ +$config['0080_00ff'][] = array('upper' => 198, 'status' => 'C', 'lower' => array(230)); /* LATIN CAPITAL LETTER AE */ +$config['0080_00ff'][] = array('upper' => 199, 'status' => 'C', 'lower' => array(231)); /* LATIN CAPITAL LETTER C WITH CEDILLA */ +$config['0080_00ff'][] = array('upper' => 200, 'status' => 'C', 'lower' => array(232)); /* LATIN CAPITAL LETTER E WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 201, 'status' => 'C', 'lower' => array(233)); /* LATIN CAPITAL LETTER E WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 202, 'status' => 'C', 'lower' => array(234)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 203, 'status' => 'C', 'lower' => array(235)); /* LATIN CAPITAL LETTER E WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 204, 'status' => 'C', 'lower' => array(236)); /* LATIN CAPITAL LETTER I WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 205, 'status' => 'C', 'lower' => array(237)); /* LATIN CAPITAL LETTER I WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 206, 'status' => 'C', 'lower' => array(238)); /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 207, 'status' => 'C', 'lower' => array(239)); /* LATIN CAPITAL LETTER I WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 208, 'status' => 'C', 'lower' => array(240)); /* LATIN CAPITAL LETTER ETH */ +$config['0080_00ff'][] = array('upper' => 209, 'status' => 'C', 'lower' => array(241)); /* LATIN CAPITAL LETTER N WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 210, 'status' => 'C', 'lower' => array(242)); /* LATIN CAPITAL LETTER O WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 211, 'status' => 'C', 'lower' => array(243)); /* LATIN CAPITAL LETTER O WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 212, 'status' => 'C', 'lower' => array(244)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 213, 'status' => 'C', 'lower' => array(245)); /* LATIN CAPITAL LETTER O WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 214, 'status' => 'C', 'lower' => array(246)); /* LATIN CAPITAL LETTER O WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 216, 'status' => 'C', 'lower' => array(248)); /* LATIN CAPITAL LETTER O WITH STROKE */ +$config['0080_00ff'][] = array('upper' => 217, 'status' => 'C', 'lower' => array(249)); /* LATIN CAPITAL LETTER U WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 218, 'status' => 'C', 'lower' => array(250)); /* LATIN CAPITAL LETTER U WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 219, 'status' => 'C', 'lower' => array(251)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 220, 'status' => 'C', 'lower' => array(252)); /* LATIN CAPITAL LETTER U WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 221, 'status' => 'C', 'lower' => array(253)); /* LATIN CAPITAL LETTER Y WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 222, 'status' => 'C', 'lower' => array(254)); /* LATIN CAPITAL LETTER THORN */ +$config['0080_00ff'][] = array('upper' => 223, 'status' => 'F', 'lower' => array(115, 115)); /* LATIN SMALL LETTER SHARP S */ diff --git a/app/Cake/Config/unicode/casefolding/0100_017f.php b/app/Cake/Config/unicode/casefolding/0100_017f.php new file mode 100644 index 00000000..fd4e0a55 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0100_017f.php @@ -0,0 +1,106 @@ + 256, 'status' => 'C', 'lower' => array(257)); /* LATIN CAPITAL LETTER A WITH MACRON */ +$config['0100_017f'][] = array('upper' => 258, 'status' => 'C', 'lower' => array(259)); /* LATIN CAPITAL LETTER A WITH BREVE */ +$config['0100_017f'][] = array('upper' => 260, 'status' => 'C', 'lower' => array(261)); /* LATIN CAPITAL LETTER A WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 262, 'status' => 'C', 'lower' => array(263)); /* LATIN CAPITAL LETTER C WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 264, 'status' => 'C', 'lower' => array(265)); /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 266, 'status' => 'C', 'lower' => array(267)); /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 268, 'status' => 'C', 'lower' => array(269)); /* LATIN CAPITAL LETTER C WITH CARON */ +$config['0100_017f'][] = array('upper' => 270, 'status' => 'C', 'lower' => array(271)); /* LATIN CAPITAL LETTER D WITH CARON */ +$config['0100_017f'][] = array('upper' => 272, 'status' => 'C', 'lower' => array(273)); /* LATIN CAPITAL LETTER D WITH STROKE */ +$config['0100_017f'][] = array('upper' => 274, 'status' => 'C', 'lower' => array(275)); /* LATIN CAPITAL LETTER E WITH MACRON */ +$config['0100_017f'][] = array('upper' => 276, 'status' => 'C', 'lower' => array(277)); /* LATIN CAPITAL LETTER E WITH BREVE */ +$config['0100_017f'][] = array('upper' => 278, 'status' => 'C', 'lower' => array(279)); /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 280, 'status' => 'C', 'lower' => array(281)); /* LATIN CAPITAL LETTER E WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 282, 'status' => 'C', 'lower' => array(283)); /* LATIN CAPITAL LETTER E WITH CARON */ +$config['0100_017f'][] = array('upper' => 284, 'status' => 'C', 'lower' => array(285)); /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 286, 'status' => 'C', 'lower' => array(287)); /* LATIN CAPITAL LETTER G WITH BREVE */ +$config['0100_017f'][] = array('upper' => 288, 'status' => 'C', 'lower' => array(289)); /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 290, 'status' => 'C', 'lower' => array(291)); /* LATIN CAPITAL LETTER G WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 292, 'status' => 'C', 'lower' => array(293)); /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 294, 'status' => 'C', 'lower' => array(295)); /* LATIN CAPITAL LETTER H WITH STROKE */ +$config['0100_017f'][] = array('upper' => 296, 'status' => 'C', 'lower' => array(297)); /* LATIN CAPITAL LETTER I WITH TILDE */ +$config['0100_017f'][] = array('upper' => 298, 'status' => 'C', 'lower' => array(299)); /* LATIN CAPITAL LETTER I WITH MACRON */ +$config['0100_017f'][] = array('upper' => 300, 'status' => 'C', 'lower' => array(301)); /* LATIN CAPITAL LETTER I WITH BREVE */ +$config['0100_017f'][] = array('upper' => 302, 'status' => 'C', 'lower' => array(303)); /* LATIN CAPITAL LETTER I WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 304, 'status' => 'F', 'lower' => array(105, 775)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 304, 'status' => 'T', 'lower' => array(105)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 306, 'status' => 'C', 'lower' => array(307)); /* LATIN CAPITAL LIGATURE IJ */ +$config['0100_017f'][] = array('upper' => 308, 'status' => 'C', 'lower' => array(309)); /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 310, 'status' => 'C', 'lower' => array(311)); /* LATIN CAPITAL LETTER K WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 313, 'status' => 'C', 'lower' => array(314)); /* LATIN CAPITAL LETTER L WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 315, 'status' => 'C', 'lower' => array(316)); /* LATIN CAPITAL LETTER L WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 317, 'status' => 'C', 'lower' => array(318)); /* LATIN CAPITAL LETTER L WITH CARON */ +$config['0100_017f'][] = array('upper' => 319, 'status' => 'C', 'lower' => array(320)); /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */ +$config['0100_017f'][] = array('upper' => 321, 'status' => 'C', 'lower' => array(322)); /* LATIN CAPITAL LETTER L WITH STROKE */ +$config['0100_017f'][] = array('upper' => 323, 'status' => 'C', 'lower' => array(324)); /* LATIN CAPITAL LETTER N WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 325, 'status' => 'C', 'lower' => array(326)); /* LATIN CAPITAL LETTER N WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 327, 'status' => 'C', 'lower' => array(328)); /* LATIN CAPITAL LETTER N WITH CARON */ +$config['0100_017f'][] = array('upper' => 329, 'status' => 'F', 'lower' => array(700, 110)); /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */ +$config['0100_017f'][] = array('upper' => 330, 'status' => 'C', 'lower' => array(331)); /* LATIN CAPITAL LETTER ENG */ +$config['0100_017f'][] = array('upper' => 332, 'status' => 'C', 'lower' => array(333)); /* LATIN CAPITAL LETTER O WITH MACRON */ +$config['0100_017f'][] = array('upper' => 334, 'status' => 'C', 'lower' => array(335)); /* LATIN CAPITAL LETTER O WITH BREVE */ +$config['0100_017f'][] = array('upper' => 336, 'status' => 'C', 'lower' => array(337)); /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +$config['0100_017f'][] = array('upper' => 338, 'status' => 'C', 'lower' => array(339)); /* LATIN CAPITAL LIGATURE OE */ +$config['0100_017f'][] = array('upper' => 340, 'status' => 'C', 'lower' => array(341)); /* LATIN CAPITAL LETTER R WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 342, 'status' => 'C', 'lower' => array(343)); /* LATIN CAPITAL LETTER R WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 344, 'status' => 'C', 'lower' => array(345)); /* LATIN CAPITAL LETTER R WITH CARON */ +$config['0100_017f'][] = array('upper' => 346, 'status' => 'C', 'lower' => array(347)); /* LATIN CAPITAL LETTER S WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 348, 'status' => 'C', 'lower' => array(349)); /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 350, 'status' => 'C', 'lower' => array(351)); /* LATIN CAPITAL LETTER S WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 352, 'status' => 'C', 'lower' => array(353)); /* LATIN CAPITAL LETTER S WITH CARON */ +$config['0100_017f'][] = array('upper' => 354, 'status' => 'C', 'lower' => array(355)); /* LATIN CAPITAL LETTER T WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 356, 'status' => 'C', 'lower' => array(357)); /* LATIN CAPITAL LETTER T WITH CARON */ +$config['0100_017f'][] = array('upper' => 358, 'status' => 'C', 'lower' => array(359)); /* LATIN CAPITAL LETTER T WITH STROKE */ +$config['0100_017f'][] = array('upper' => 360, 'status' => 'C', 'lower' => array(361)); /* LATIN CAPITAL LETTER U WITH TILDE */ +$config['0100_017f'][] = array('upper' => 362, 'status' => 'C', 'lower' => array(363)); /* LATIN CAPITAL LETTER U WITH MACRON */ +$config['0100_017f'][] = array('upper' => 364, 'status' => 'C', 'lower' => array(365)); /* LATIN CAPITAL LETTER U WITH BREVE */ +$config['0100_017f'][] = array('upper' => 366, 'status' => 'C', 'lower' => array(367)); /* LATIN CAPITAL LETTER U WITH RING ABOVE */ +$config['0100_017f'][] = array('upper' => 368, 'status' => 'C', 'lower' => array(369)); /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +$config['0100_017f'][] = array('upper' => 370, 'status' => 'C', 'lower' => array(371)); /* LATIN CAPITAL LETTER U WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 372, 'status' => 'C', 'lower' => array(373)); /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 374, 'status' => 'C', 'lower' => array(375)); /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 376, 'status' => 'C', 'lower' => array(255)); /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ +$config['0100_017f'][] = array('upper' => 377, 'status' => 'C', 'lower' => array(378)); /* LATIN CAPITAL LETTER Z WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 379, 'status' => 'C', 'lower' => array(380)); /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 381, 'status' => 'C', 'lower' => array(382)); /* LATIN CAPITAL LETTER Z WITH CARON */ +$config['0100_017f'][] = array('upper' => 383, 'status' => 'C', 'lower' => array(115)); /* LATIN SMALL LETTER LONG S */ diff --git a/app/Cake/Config/unicode/casefolding/0180_024F.php b/app/Cake/Config/unicode/casefolding/0180_024F.php new file mode 100644 index 00000000..e3250ad8 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0180_024F.php @@ -0,0 +1,148 @@ + 385, 'status' => 'C', 'lower' => array(595)); /* LATIN CAPITAL LETTER B WITH HOOK */ +$config['0180_024F'][] = array('upper' => 386, 'status' => 'C', 'lower' => array(387)); /* LATIN CAPITAL LETTER B WITH TOPBAR */ +$config['0180_024F'][] = array('upper' => 388, 'status' => 'C', 'lower' => array(389)); /* LATIN CAPITAL LETTER TONE SIX */ +$config['0180_024F'][] = array('upper' => 390, 'status' => 'C', 'lower' => array(596)); /* LATIN CAPITAL LETTER OPEN O */ +$config['0180_024F'][] = array('upper' => 391, 'status' => 'C', 'lower' => array(392)); /* LATIN CAPITAL LETTER C WITH HOOK */ +$config['0180_024F'][] = array('upper' => 393, 'status' => 'C', 'lower' => array(598)); /* LATIN CAPITAL LETTER AFRICAN D */ +$config['0180_024F'][] = array('upper' => 394, 'status' => 'C', 'lower' => array(599)); /* LATIN CAPITAL LETTER D WITH HOOK */ +$config['0180_024F'][] = array('upper' => 395, 'status' => 'C', 'lower' => array(396)); /* LATIN CAPITAL LETTER D WITH TOPBAR */ +$config['0180_024F'][] = array('upper' => 398, 'status' => 'C', 'lower' => array(477)); /* LATIN CAPITAL LETTER REVERSED E */ +$config['0180_024F'][] = array('upper' => 399, 'status' => 'C', 'lower' => array(601)); /* LATIN CAPITAL LETTER SCHWA */ +$config['0180_024F'][] = array('upper' => 400, 'status' => 'C', 'lower' => array(603)); /* LATIN CAPITAL LETTER OPEN E */ +$config['0180_024F'][] = array('upper' => 401, 'status' => 'C', 'lower' => array(402)); /* LATIN CAPITAL LETTER F WITH HOOK */ +$config['0180_024F'][] = array('upper' => 403, 'status' => 'C', 'lower' => array(608)); /* LATIN CAPITAL LETTER G WITH HOOK */ +$config['0180_024F'][] = array('upper' => 404, 'status' => 'C', 'lower' => array(611)); /* LATIN CAPITAL LETTER GAMMA */ +$config['0180_024F'][] = array('upper' => 406, 'status' => 'C', 'lower' => array(617)); /* LATIN CAPITAL LETTER IOTA */ +$config['0180_024F'][] = array('upper' => 407, 'status' => 'C', 'lower' => array(616)); /* LATIN CAPITAL LETTER I WITH STROKE */ +$config['0180_024F'][] = array('upper' => 408, 'status' => 'C', 'lower' => array(409)); /* LATIN CAPITAL LETTER K WITH HOOK */ +$config['0180_024F'][] = array('upper' => 412, 'status' => 'C', 'lower' => array(623)); /* LATIN CAPITAL LETTER TURNED M */ +$config['0180_024F'][] = array('upper' => 413, 'status' => 'C', 'lower' => array(626)); /* LATIN CAPITAL LETTER N WITH LEFT HOOK */ +$config['0180_024F'][] = array('upper' => 415, 'status' => 'C', 'lower' => array(629)); /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE */ +$config['0180_024F'][] = array('upper' => 416, 'status' => 'C', 'lower' => array(417)); /* LATIN CAPITAL LETTER O WITH HORN */ +$config['0180_024F'][] = array('upper' => 418, 'status' => 'C', 'lower' => array(419)); /* LATIN CAPITAL LETTER OI */ +$config['0180_024F'][] = array('upper' => 420, 'status' => 'C', 'lower' => array(421)); /* LATIN CAPITAL LETTER P WITH HOOK */ +$config['0180_024F'][] = array('upper' => 422, 'status' => 'C', 'lower' => array(640)); /* LATIN LETTER YR */ +$config['0180_024F'][] = array('upper' => 423, 'status' => 'C', 'lower' => array(424)); /* LATIN CAPITAL LETTER TONE TWO */ +$config['0180_024F'][] = array('upper' => 425, 'status' => 'C', 'lower' => array(643)); /* LATIN CAPITAL LETTER ESH */ +$config['0180_024F'][] = array('upper' => 428, 'status' => 'C', 'lower' => array(429)); /* LATIN CAPITAL LETTER T WITH HOOK */ +$config['0180_024F'][] = array('upper' => 430, 'status' => 'C', 'lower' => array(648)); /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */ +$config['0180_024F'][] = array('upper' => 431, 'status' => 'C', 'lower' => array(432)); /* LATIN CAPITAL LETTER U WITH HORN */ +$config['0180_024F'][] = array('upper' => 433, 'status' => 'C', 'lower' => array(650)); /* LATIN CAPITAL LETTER UPSILON */ +$config['0180_024F'][] = array('upper' => 434, 'status' => 'C', 'lower' => array(651)); /* LATIN CAPITAL LETTER V WITH HOOK */ +$config['0180_024F'][] = array('upper' => 435, 'status' => 'C', 'lower' => array(436)); /* LATIN CAPITAL LETTER Y WITH HOOK */ +$config['0180_024F'][] = array('upper' => 437, 'status' => 'C', 'lower' => array(438)); /* LATIN CAPITAL LETTER Z WITH STROKE */ +$config['0180_024F'][] = array('upper' => 439, 'status' => 'C', 'lower' => array(658)); /* LATIN CAPITAL LETTER EZH */ +$config['0180_024F'][] = array('upper' => 440, 'status' => 'C', 'lower' => array(441)); /* LATIN CAPITAL LETTER EZH REVERSED */ +$config['0180_024F'][] = array('upper' => 444, 'status' => 'C', 'lower' => array(445)); /* LATIN CAPITAL LETTER TONE FIVE */ +$config['0180_024F'][] = array('upper' => 452, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER DZ WITH CARON */ +$config['0180_024F'][] = array('upper' => 453, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON */ +$config['0180_024F'][] = array('upper' => 455, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER LJ */ +$config['0180_024F'][] = array('upper' => 456, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER L WITH SMALL LETTER J */ +$config['0180_024F'][] = array('upper' => 458, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER NJ */ +$config['0180_024F'][] = array('upper' => 459, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER N WITH SMALL LETTER J */ +$config['0180_024F'][] = array('upper' => 461, 'status' => 'C', 'lower' => array(462)); /* LATIN CAPITAL LETTER A WITH CARON */ +$config['0180_024F'][] = array('upper' => 463, 'status' => 'C', 'lower' => array(464)); /* LATIN CAPITAL LETTER I WITH CARON */ +$config['0180_024F'][] = array('upper' => 465, 'status' => 'C', 'lower' => array(466)); /* LATIN CAPITAL LETTER O WITH CARON */ +$config['0180_024F'][] = array('upper' => 467, 'status' => 'C', 'lower' => array(468)); /* LATIN CAPITAL LETTER U WITH CARON */ +$config['0180_024F'][] = array('upper' => 469, 'status' => 'C', 'lower' => array(470)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 471, 'status' => 'C', 'lower' => array(472)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */ +$config['0180_024F'][] = array('upper' => 473, 'status' => 'C', 'lower' => array(474)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */ +$config['0180_024F'][] = array('upper' => 475, 'status' => 'C', 'lower' => array(476)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */ +$config['0180_024F'][] = array('upper' => 478, 'status' => 'C', 'lower' => array(479)); /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 480, 'status' => 'C', 'lower' => array(481)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */ +$config['0180_024F'][] = array('upper' => 482, 'status' => 'C', 'lower' => array(483)); /* LATIN CAPITAL LETTER AE WITH MACRON */ +$config['0180_024F'][] = array('upper' => 484, 'status' => 'C', 'lower' => array(485)); /* LATIN CAPITAL LETTER G WITH STROKE */ +$config['0180_024F'][] = array('upper' => 486, 'status' => 'C', 'lower' => array(487)); /* LATIN CAPITAL LETTER G WITH CARON */ +$config['0180_024F'][] = array('upper' => 488, 'status' => 'C', 'lower' => array(489)); /* LATIN CAPITAL LETTER K WITH CARON */ +$config['0180_024F'][] = array('upper' => 490, 'status' => 'C', 'lower' => array(491)); /* LATIN CAPITAL LETTER O WITH OGONEK */ +$config['0180_024F'][] = array('upper' => 492, 'status' => 'C', 'lower' => array(493)); /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */ +$config['0180_024F'][] = array('upper' => 494, 'status' => 'C', 'lower' => array(495)); /* LATIN CAPITAL LETTER EZH WITH CARON */ +$config['0180_024F'][] = array('upper' => 496, 'status' => 'F', 'lower' => array(106, 780)); /* LATIN SMALL LETTER J WITH CARON */ +$config['0180_024F'][] = array('upper' => 497, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER DZ */ +$config['0180_024F'][] = array('upper' => 498, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z */ +$config['0180_024F'][] = array('upper' => 500, 'status' => 'C', 'lower' => array(501)); /* LATIN CAPITAL LETTER G WITH ACUTE */ +$config['0180_024F'][] = array('upper' => 502, 'status' => 'C', 'lower' => array(405)); /* LATIN CAPITAL LETTER HWAIR */ +$config['0180_024F'][] = array('upper' => 503, 'status' => 'C', 'lower' => array(447)); /* LATIN CAPITAL LETTER WYNN */ +$config['0180_024F'][] = array('upper' => 504, 'status' => 'C', 'lower' => array(505)); /* LATIN CAPITAL LETTER N WITH GRAVE */ +$config['0180_024F'][] = array('upper' => 506, 'status' => 'C', 'lower' => array(507)); /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */ +$config['0180_024F'][] = array('upper' => 508, 'status' => 'C', 'lower' => array(509)); /* LATIN CAPITAL LETTER AE WITH ACUTE */ +$config['0180_024F'][] = array('upper' => 510, 'status' => 'C', 'lower' => array(511)); /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */ +$config['0180_024F'][] = array('upper' => 512, 'status' => 'C', 'lower' => array(513)); /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 514, 'status' => 'C', 'lower' => array(515)); /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 516, 'status' => 'C', 'lower' => array(517)); /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 518, 'status' => 'C', 'lower' => array(519)); /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 520, 'status' => 'C', 'lower' => array(521)); /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 522, 'status' => 'C', 'lower' => array(523)); /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 524, 'status' => 'C', 'lower' => array(525)); /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 526, 'status' => 'C', 'lower' => array(527)); /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 528, 'status' => 'C', 'lower' => array(529)); /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 530, 'status' => 'C', 'lower' => array(531)); /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 532, 'status' => 'C', 'lower' => array(533)); /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 534, 'status' => 'C', 'lower' => array(535)); /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 536, 'status' => 'C', 'lower' => array(537)); /* LATIN CAPITAL LETTER S WITH COMMA BELOW */ +$config['0180_024F'][] = array('upper' => 538, 'status' => 'C', 'lower' => array(539)); /* LATIN CAPITAL LETTER T WITH COMMA BELOW */ +$config['0180_024F'][] = array('upper' => 540, 'status' => 'C', 'lower' => array(541)); /* LATIN CAPITAL LETTER YOGH */ +$config['0180_024F'][] = array('upper' => 542, 'status' => 'C', 'lower' => array(543)); /* LATIN CAPITAL LETTER H WITH CARON */ +$config['0180_024F'][] = array('upper' => 544, 'status' => 'C', 'lower' => array(414)); /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */ +$config['0180_024F'][] = array('upper' => 546, 'status' => 'C', 'lower' => array(547)); /* LATIN CAPITAL LETTER OU */ +$config['0180_024F'][] = array('upper' => 548, 'status' => 'C', 'lower' => array(549)); /* LATIN CAPITAL LETTER Z WITH HOOK */ +$config['0180_024F'][] = array('upper' => 550, 'status' => 'C', 'lower' => array(551)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE */ +$config['0180_024F'][] = array('upper' => 552, 'status' => 'C', 'lower' => array(553)); /* LATIN CAPITAL LETTER E WITH CEDILLA */ +$config['0180_024F'][] = array('upper' => 554, 'status' => 'C', 'lower' => array(555)); /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 556, 'status' => 'C', 'lower' => array(557)); /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */ +$config['0180_024F'][] = array('upper' => 558, 'status' => 'C', 'lower' => array(559)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE */ +$config['0180_024F'][] = array('upper' => 560, 'status' => 'C', 'lower' => array(561)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */ +$config['0180_024F'][] = array('upper' => 562, 'status' => 'C', 'lower' => array(563)); /* LATIN CAPITAL LETTER Y WITH MACRON */ +$config['0180_024F'][] = array('upper' => 570, 'status' => 'C', 'lower' => array(11365)); /* LATIN CAPITAL LETTER A WITH STROKE */ +$config['0180_024F'][] = array('upper' => 571, 'status' => 'C', 'lower' => array(572)); /* LATIN CAPITAL LETTER C WITH STROKE */ +$config['0180_024F'][] = array('upper' => 573, 'status' => 'C', 'lower' => array(410)); /* LATIN CAPITAL LETTER L WITH BAR */ +$config['0180_024F'][] = array('upper' => 574, 'status' => 'C', 'lower' => array(11366)); /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */ +$config['0180_024F'][] = array('upper' => 577, 'status' => 'C', 'lower' => array(578)); /* LATIN CAPITAL LETTER GLOTTAL STOP */ +$config['0180_024F'][] = array('upper' => 579, 'status' => 'C', 'lower' => array(384)); /* LATIN CAPITAL LETTER B WITH STROKE */ +$config['0180_024F'][] = array('upper' => 580, 'status' => 'C', 'lower' => array(649)); /* LATIN CAPITAL LETTER U BAR */ +$config['0180_024F'][] = array('upper' => 581, 'status' => 'C', 'lower' => array(652)); /* LATIN CAPITAL LETTER TURNED V */ +$config['0180_024F'][] = array('upper' => 582, 'status' => 'C', 'lower' => array(583)); /* LATIN CAPITAL LETTER E WITH STROKE */ +$config['0180_024F'][] = array('upper' => 584, 'status' => 'C', 'lower' => array(585)); /* LATIN CAPITAL LETTER J WITH STROKE */ +$config['0180_024F'][] = array('upper' => 586, 'status' => 'C', 'lower' => array(587)); /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL */ +$config['0180_024F'][] = array('upper' => 588, 'status' => 'C', 'lower' => array(589)); /* LATIN CAPITAL LETTER R WITH STROKE */ +$config['0180_024F'][] = array('upper' => 590, 'status' => 'C', 'lower' => array(591)); /* LATIN CAPITAL LETTER Y WITH STROKE */ diff --git a/app/Cake/Config/unicode/casefolding/0250_02af.php b/app/Cake/Config/unicode/casefolding/0250_02af.php new file mode 100644 index 00000000..c75f2ed8 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0250_02af.php @@ -0,0 +1,41 @@ + 422, 'status' => 'C', 'lower' => array(640)); diff --git a/app/Cake/Config/unicode/casefolding/0370_03ff.php b/app/Cake/Config/unicode/casefolding/0370_03ff.php new file mode 100644 index 00000000..0c35226c --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0370_03ff.php @@ -0,0 +1,102 @@ + 902, 'status' => 'C', 'lower' => array(940)); /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 904, 'status' => 'C', 'lower' => array(941)); /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 905, 'status' => 'C', 'lower' => array(942)); /* GREEK CAPITAL LETTER ETA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 906, 'status' => 'C', 'lower' => array(943)); /* GREEK CAPITAL LETTER IOTA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 908, 'status' => 'C', 'lower' => array(972)); /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 910, 'status' => 'C', 'lower' => array(973)); /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 911, 'status' => 'C', 'lower' => array(974)); /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ +//$config['0370_03ff'][] = array('upper' => 912, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +$config['0370_03ff'][] = array('upper' => 913, 'status' => 'C', 'lower' => array(945)); /* GREEK CAPITAL LETTER ALPHA */ +$config['0370_03ff'][] = array('upper' => 914, 'status' => 'C', 'lower' => array(946)); /* GREEK CAPITAL LETTER BETA */ +$config['0370_03ff'][] = array('upper' => 915, 'status' => 'C', 'lower' => array(947)); /* GREEK CAPITAL LETTER GAMMA */ +$config['0370_03ff'][] = array('upper' => 916, 'status' => 'C', 'lower' => array(948)); /* GREEK CAPITAL LETTER DELTA */ +$config['0370_03ff'][] = array('upper' => 917, 'status' => 'C', 'lower' => array(949)); /* GREEK CAPITAL LETTER EPSILON */ +$config['0370_03ff'][] = array('upper' => 918, 'status' => 'C', 'lower' => array(950)); /* GREEK CAPITAL LETTER ZETA */ +$config['0370_03ff'][] = array('upper' => 919, 'status' => 'C', 'lower' => array(951)); /* GREEK CAPITAL LETTER ETA */ +$config['0370_03ff'][] = array('upper' => 920, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL LETTER THETA */ +$config['0370_03ff'][] = array('upper' => 921, 'status' => 'C', 'lower' => array(953)); /* GREEK CAPITAL LETTER IOTA */ +$config['0370_03ff'][] = array('upper' => 922, 'status' => 'C', 'lower' => array(954)); /* GREEK CAPITAL LETTER KAPPA */ +$config['0370_03ff'][] = array('upper' => 923, 'status' => 'C', 'lower' => array(955)); /* GREEK CAPITAL LETTER LAMDA */ +$config['0370_03ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(956)); /* GREEK CAPITAL LETTER MU */ +$config['0370_03ff'][] = array('upper' => 925, 'status' => 'C', 'lower' => array(957)); /* GREEK CAPITAL LETTER NU */ +$config['0370_03ff'][] = array('upper' => 926, 'status' => 'C', 'lower' => array(958)); /* GREEK CAPITAL LETTER XI */ +$config['0370_03ff'][] = array('upper' => 927, 'status' => 'C', 'lower' => array(959)); /* GREEK CAPITAL LETTER OMICRON */ +$config['0370_03ff'][] = array('upper' => 928, 'status' => 'C', 'lower' => array(960)); /* GREEK CAPITAL LETTER PI */ +$config['0370_03ff'][] = array('upper' => 929, 'status' => 'C', 'lower' => array(961)); /* GREEK CAPITAL LETTER RHO */ +$config['0370_03ff'][] = array('upper' => 931, 'status' => 'C', 'lower' => array(963)); /* GREEK CAPITAL LETTER SIGMA */ +$config['0370_03ff'][] = array('upper' => 932, 'status' => 'C', 'lower' => array(964)); /* GREEK CAPITAL LETTER TAU */ +$config['0370_03ff'][] = array('upper' => 933, 'status' => 'C', 'lower' => array(965)); /* GREEK CAPITAL LETTER UPSILON */ +$config['0370_03ff'][] = array('upper' => 934, 'status' => 'C', 'lower' => array(966)); /* GREEK CAPITAL LETTER PHI */ +$config['0370_03ff'][] = array('upper' => 935, 'status' => 'C', 'lower' => array(967)); /* GREEK CAPITAL LETTER CHI */ +$config['0370_03ff'][] = array('upper' => 936, 'status' => 'C', 'lower' => array(968)); /* GREEK CAPITAL LETTER PSI */ +$config['0370_03ff'][] = array('upper' => 937, 'status' => 'C', 'lower' => array(969)); /* GREEK CAPITAL LETTER OMEGA */ +$config['0370_03ff'][] = array('upper' => 938, 'status' => 'C', 'lower' => array(970)); /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +$config['0370_03ff'][] = array('upper' => 939, 'status' => 'C', 'lower' => array(971)); /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +$config['0370_03ff'][] = array('upper' => 944, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +$config['0370_03ff'][] = array('upper' => 962, 'status' => 'C', 'lower' => array(963)); /* GREEK SMALL LETTER FINAL SIGMA */ +$config['0370_03ff'][] = array('upper' => 976, 'status' => 'C', 'lower' => array(946)); /* GREEK BETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 977, 'status' => 'C', 'lower' => array(952)); /* GREEK THETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 981, 'status' => 'C', 'lower' => array(966)); /* GREEK PHI SYMBOL */ +$config['0370_03ff'][] = array('upper' => 982, 'status' => 'C', 'lower' => array(960)); /* GREEK PI SYMBOL */ +$config['0370_03ff'][] = array('upper' => 984, 'status' => 'C', 'lower' => array(985)); /* GREEK LETTER ARCHAIC KOPPA */ +$config['0370_03ff'][] = array('upper' => 986, 'status' => 'C', 'lower' => array(987)); /* GREEK LETTER STIGMA */ +$config['0370_03ff'][] = array('upper' => 988, 'status' => 'C', 'lower' => array(989)); /* GREEK LETTER DIGAMMA */ +$config['0370_03ff'][] = array('upper' => 990, 'status' => 'C', 'lower' => array(991)); /* GREEK LETTER KOPPA */ +$config['0370_03ff'][] = array('upper' => 992, 'status' => 'C', 'lower' => array(993)); /* GREEK LETTER SAMPI */ +$config['0370_03ff'][] = array('upper' => 994, 'status' => 'C', 'lower' => array(995)); /* COPTIC CAPITAL LETTER SHEI */ +$config['0370_03ff'][] = array('upper' => 996, 'status' => 'C', 'lower' => array(997)); /* COPTIC CAPITAL LETTER FEI */ +$config['0370_03ff'][] = array('upper' => 998, 'status' => 'C', 'lower' => array(999)); /* COPTIC CAPITAL LETTER KHEI */ +$config['0370_03ff'][] = array('upper' => 1000, 'status' => 'C', 'lower' => array(1001)); /* COPTIC CAPITAL LETTER HORI */ +$config['0370_03ff'][] = array('upper' => 1002, 'status' => 'C', 'lower' => array(1003)); /* COPTIC CAPITAL LETTER GANGIA */ +$config['0370_03ff'][] = array('upper' => 1004, 'status' => 'C', 'lower' => array(1005)); /* COPTIC CAPITAL LETTER SHIMA */ +$config['0370_03ff'][] = array('upper' => 1006, 'status' => 'C', 'lower' => array(1007)); /* COPTIC CAPITAL LETTER DEI */ +$config['0370_03ff'][] = array('upper' => 1008, 'status' => 'C', 'lower' => array(954)); /* GREEK KAPPA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1009, 'status' => 'C', 'lower' => array(961)); /* GREEK RHO SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1012, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL THETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1013, 'status' => 'C', 'lower' => array(949)); /* GREEK LUNATE EPSILON SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1015, 'status' => 'C', 'lower' => array(1016)); /* GREEK CAPITAL LETTER SHO */ +$config['0370_03ff'][] = array('upper' => 1017, 'status' => 'C', 'lower' => array(1010)); /* GREEK CAPITAL LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1018, 'status' => 'C', 'lower' => array(1019)); /* GREEK CAPITAL LETTER SAN */ +$config['0370_03ff'][] = array('upper' => 1021, 'status' => 'C', 'lower' => array(891)); /* GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1022, 'status' => 'C', 'lower' => array(892)); /* GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1023, 'status' => 'C', 'lower' => array(893)); /* GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL */ diff --git a/app/Cake/Config/unicode/casefolding/0400_04ff.php b/app/Cake/Config/unicode/casefolding/0400_04ff.php new file mode 100644 index 00000000..b267b3fd --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0400_04ff.php @@ -0,0 +1,164 @@ + 1024, 'status' => 'C', 'lower' => array(1104)); /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */ +$config['0400_04ff'][] = array('upper' => 1025, 'status' => 'C', 'lower' => array(1105)); /* CYRILLIC CAPITAL LETTER IO */ +$config['0400_04ff'][] = array('upper' => 1026, 'status' => 'C', 'lower' => array(1106)); /* CYRILLIC CAPITAL LETTER DJE */ +$config['0400_04ff'][] = array('upper' => 1027, 'status' => 'C', 'lower' => array(1107)); /* CYRILLIC CAPITAL LETTER GJE */ +$config['0400_04ff'][] = array('upper' => 1028, 'status' => 'C', 'lower' => array(1108)); /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +$config['0400_04ff'][] = array('upper' => 1029, 'status' => 'C', 'lower' => array(1109)); /* CYRILLIC CAPITAL LETTER DZE */ +$config['0400_04ff'][] = array('upper' => 1030, 'status' => 'C', 'lower' => array(1110)); /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +$config['0400_04ff'][] = array('upper' => 1031, 'status' => 'C', 'lower' => array(1111)); /* CYRILLIC CAPITAL LETTER YI */ +$config['0400_04ff'][] = array('upper' => 1032, 'status' => 'C', 'lower' => array(1112)); /* CYRILLIC CAPITAL LETTER JE */ +$config['0400_04ff'][] = array('upper' => 1033, 'status' => 'C', 'lower' => array(1113)); /* CYRILLIC CAPITAL LETTER LJE */ +$config['0400_04ff'][] = array('upper' => 1034, 'status' => 'C', 'lower' => array(1114)); /* CYRILLIC CAPITAL LETTER NJE */ +$config['0400_04ff'][] = array('upper' => 1035, 'status' => 'C', 'lower' => array(1115)); /* CYRILLIC CAPITAL LETTER TSHE */ +$config['0400_04ff'][] = array('upper' => 1036, 'status' => 'C', 'lower' => array(1116)); /* CYRILLIC CAPITAL LETTER KJE */ +$config['0400_04ff'][] = array('upper' => 1037, 'status' => 'C', 'lower' => array(1117)); /* CYRILLIC CAPITAL LETTER I WITH GRAVE */ +$config['0400_04ff'][] = array('upper' => 1038, 'status' => 'C', 'lower' => array(1118)); /* CYRILLIC CAPITAL LETTER SHORT U */ +$config['0400_04ff'][] = array('upper' => 1039, 'status' => 'C', 'lower' => array(1119)); /* CYRILLIC CAPITAL LETTER DZHE */ +$config['0400_04ff'][] = array('upper' => 1040, 'status' => 'C', 'lower' => array(1072)); /* CYRILLIC CAPITAL LETTER A */ +$config['0400_04ff'][] = array('upper' => 1041, 'status' => 'C', 'lower' => array(1073)); /* CYRILLIC CAPITAL LETTER BE */ +$config['0400_04ff'][] = array('upper' => 1042, 'status' => 'C', 'lower' => array(1074)); /* CYRILLIC CAPITAL LETTER VE */ +$config['0400_04ff'][] = array('upper' => 1043, 'status' => 'C', 'lower' => array(1075)); /* CYRILLIC CAPITAL LETTER GHE */ +$config['0400_04ff'][] = array('upper' => 1044, 'status' => 'C', 'lower' => array(1076)); /* CYRILLIC CAPITAL LETTER DE */ +$config['0400_04ff'][] = array('upper' => 1045, 'status' => 'C', 'lower' => array(1077)); /* CYRILLIC CAPITAL LETTER IE */ +$config['0400_04ff'][] = array('upper' => 1046, 'status' => 'C', 'lower' => array(1078)); /* CYRILLIC CAPITAL LETTER ZHE */ +$config['0400_04ff'][] = array('upper' => 1047, 'status' => 'C', 'lower' => array(1079)); /* CYRILLIC CAPITAL LETTER ZE */ +$config['0400_04ff'][] = array('upper' => 1048, 'status' => 'C', 'lower' => array(1080)); /* CYRILLIC CAPITAL LETTER I */ +$config['0400_04ff'][] = array('upper' => 1049, 'status' => 'C', 'lower' => array(1081)); /* CYRILLIC CAPITAL LETTER SHORT I */ +$config['0400_04ff'][] = array('upper' => 1050, 'status' => 'C', 'lower' => array(1082)); /* CYRILLIC CAPITAL LETTER KA */ +$config['0400_04ff'][] = array('upper' => 1051, 'status' => 'C', 'lower' => array(1083)); /* CYRILLIC CAPITAL LETTER EL */ +$config['0400_04ff'][] = array('upper' => 1052, 'status' => 'C', 'lower' => array(1084)); /* CYRILLIC CAPITAL LETTER EM */ +$config['0400_04ff'][] = array('upper' => 1053, 'status' => 'C', 'lower' => array(1085)); /* CYRILLIC CAPITAL LETTER EN */ +$config['0400_04ff'][] = array('upper' => 1054, 'status' => 'C', 'lower' => array(1086)); /* CYRILLIC CAPITAL LETTER O */ +$config['0400_04ff'][] = array('upper' => 1055, 'status' => 'C', 'lower' => array(1087)); /* CYRILLIC CAPITAL LETTER PE */ +$config['0400_04ff'][] = array('upper' => 1056, 'status' => 'C', 'lower' => array(1088)); /* CYRILLIC CAPITAL LETTER ER */ +$config['0400_04ff'][] = array('upper' => 1057, 'status' => 'C', 'lower' => array(1089)); /* CYRILLIC CAPITAL LETTER ES */ +$config['0400_04ff'][] = array('upper' => 1058, 'status' => 'C', 'lower' => array(1090)); /* CYRILLIC CAPITAL LETTER TE */ +$config['0400_04ff'][] = array('upper' => 1059, 'status' => 'C', 'lower' => array(1091)); /* CYRILLIC CAPITAL LETTER U */ +$config['0400_04ff'][] = array('upper' => 1060, 'status' => 'C', 'lower' => array(1092)); /* CYRILLIC CAPITAL LETTER EF */ +$config['0400_04ff'][] = array('upper' => 1061, 'status' => 'C', 'lower' => array(1093)); /* CYRILLIC CAPITAL LETTER HA */ +$config['0400_04ff'][] = array('upper' => 1062, 'status' => 'C', 'lower' => array(1094)); /* CYRILLIC CAPITAL LETTER TSE */ +$config['0400_04ff'][] = array('upper' => 1063, 'status' => 'C', 'lower' => array(1095)); /* CYRILLIC CAPITAL LETTER CHE */ +$config['0400_04ff'][] = array('upper' => 1064, 'status' => 'C', 'lower' => array(1096)); /* CYRILLIC CAPITAL LETTER SHA */ +$config['0400_04ff'][] = array('upper' => 1065, 'status' => 'C', 'lower' => array(1097)); /* CYRILLIC CAPITAL LETTER SHCHA */ +$config['0400_04ff'][] = array('upper' => 1066, 'status' => 'C', 'lower' => array(1098)); /* CYRILLIC CAPITAL LETTER HARD SIGN */ +$config['0400_04ff'][] = array('upper' => 1067, 'status' => 'C', 'lower' => array(1099)); /* CYRILLIC CAPITAL LETTER YERU */ +$config['0400_04ff'][] = array('upper' => 1068, 'status' => 'C', 'lower' => array(1100)); /* CYRILLIC CAPITAL LETTER SOFT SIGN */ +$config['0400_04ff'][] = array('upper' => 1069, 'status' => 'C', 'lower' => array(1101)); /* CYRILLIC CAPITAL LETTER E */ +$config['0400_04ff'][] = array('upper' => 1070, 'status' => 'C', 'lower' => array(1102)); /* CYRILLIC CAPITAL LETTER YU */ +$config['0400_04ff'][] = array('upper' => 1071, 'status' => 'C', 'lower' => array(1103)); /* CYRILLIC CAPITAL LETTER YA */ +$config['0400_04ff'][] = array('upper' => 1120, 'status' => 'C', 'lower' => array(1121)); /* CYRILLIC CAPITAL LETTER OMEGA */ +$config['0400_04ff'][] = array('upper' => 1122, 'status' => 'C', 'lower' => array(1123)); /* CYRILLIC CAPITAL LETTER YAT */ +$config['0400_04ff'][] = array('upper' => 1124, 'status' => 'C', 'lower' => array(1125)); /* CYRILLIC CAPITAL LETTER IOTIFIED E */ +$config['0400_04ff'][] = array('upper' => 1126, 'status' => 'C', 'lower' => array(1127)); /* CYRILLIC CAPITAL LETTER LITTLE YUS */ +$config['0400_04ff'][] = array('upper' => 1128, 'status' => 'C', 'lower' => array(1129)); /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */ +$config['0400_04ff'][] = array('upper' => 1130, 'status' => 'C', 'lower' => array(1131)); /* CYRILLIC CAPITAL LETTER BIG YUS */ +$config['0400_04ff'][] = array('upper' => 1132, 'status' => 'C', 'lower' => array(1133)); /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */ +$config['0400_04ff'][] = array('upper' => 1134, 'status' => 'C', 'lower' => array(1135)); /* CYRILLIC CAPITAL LETTER KSI */ +$config['0400_04ff'][] = array('upper' => 1136, 'status' => 'C', 'lower' => array(1137)); /* CYRILLIC CAPITAL LETTER PSI */ +$config['0400_04ff'][] = array('upper' => 1138, 'status' => 'C', 'lower' => array(1139)); /* CYRILLIC CAPITAL LETTER FITA */ +$config['0400_04ff'][] = array('upper' => 1140, 'status' => 'C', 'lower' => array(1141)); /* CYRILLIC CAPITAL LETTER IZHITSA */ +$config['0400_04ff'][] = array('upper' => 1142, 'status' => 'C', 'lower' => array(1143)); /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ +$config['0400_04ff'][] = array('upper' => 1144, 'status' => 'C', 'lower' => array(1145)); /* CYRILLIC CAPITAL LETTER UK */ +$config['0400_04ff'][] = array('upper' => 1146, 'status' => 'C', 'lower' => array(1147)); /* CYRILLIC CAPITAL LETTER ROUND OMEGA */ +$config['0400_04ff'][] = array('upper' => 1148, 'status' => 'C', 'lower' => array(1149)); /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */ +$config['0400_04ff'][] = array('upper' => 1150, 'status' => 'C', 'lower' => array(1151)); /* CYRILLIC CAPITAL LETTER OT */ +$config['0400_04ff'][] = array('upper' => 1152, 'status' => 'C', 'lower' => array(1153)); /* CYRILLIC CAPITAL LETTER KOPPA */ +$config['0400_04ff'][] = array('upper' => 1162, 'status' => 'C', 'lower' => array(1163)); /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1164, 'status' => 'C', 'lower' => array(1165)); /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ +$config['0400_04ff'][] = array('upper' => 1166, 'status' => 'C', 'lower' => array(1167)); /* CYRILLIC CAPITAL LETTER ER WITH TICK */ +$config['0400_04ff'][] = array('upper' => 1168, 'status' => 'C', 'lower' => array(1169)); /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ +$config['0400_04ff'][] = array('upper' => 1170, 'status' => 'C', 'lower' => array(1171)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1172, 'status' => 'C', 'lower' => array(1173)); /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */ +$config['0400_04ff'][] = array('upper' => 1174, 'status' => 'C', 'lower' => array(1175)); /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1176, 'status' => 'C', 'lower' => array(1177)); /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1178, 'status' => 'C', 'lower' => array(1179)); /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1180, 'status' => 'C', 'lower' => array(1181)); /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */ +$config['0400_04ff'][] = array('upper' => 1182, 'status' => 'C', 'lower' => array(1183)); /* CYRILLIC CAPITAL LETTER KA WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1184, 'status' => 'C', 'lower' => array(1185)); /* CYRILLIC CAPITAL LETTER BASHKIR KA */ +$config['0400_04ff'][] = array('upper' => 1186, 'status' => 'C', 'lower' => array(1187)); /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1188, 'status' => 'C', 'lower' => array(1189)); /* CYRILLIC CAPITAL LIGATURE EN GHE */ +$config['0400_04ff'][] = array('upper' => 1190, 'status' => 'C', 'lower' => array(1191)); /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */ +$config['0400_04ff'][] = array('upper' => 1192, 'status' => 'C', 'lower' => array(1193)); /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */ +$config['0400_04ff'][] = array('upper' => 1194, 'status' => 'C', 'lower' => array(1195)); /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1196, 'status' => 'C', 'lower' => array(1197)); /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1198, 'status' => 'C', 'lower' => array(1199)); /* CYRILLIC CAPITAL LETTER STRAIGHT U */ +$config['0400_04ff'][] = array('upper' => 1200, 'status' => 'C', 'lower' => array(1201)); /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1202, 'status' => 'C', 'lower' => array(1203)); /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1204, 'status' => 'C', 'lower' => array(1205)); /* CYRILLIC CAPITAL LIGATURE TE TSE */ +$config['0400_04ff'][] = array('upper' => 1206, 'status' => 'C', 'lower' => array(1207)); /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1208, 'status' => 'C', 'lower' => array(1209)); /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */ +$config['0400_04ff'][] = array('upper' => 1210, 'status' => 'C', 'lower' => array(1211)); /* CYRILLIC CAPITAL LETTER SHHA */ +$config['0400_04ff'][] = array('upper' => 1212, 'status' => 'C', 'lower' => array(1213)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ +$config['0400_04ff'][] = array('upper' => 1214, 'status' => 'C', 'lower' => array(1215)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1216, 'status' => 'C', 'lower' => array(1231)); /* CYRILLIC LETTER PALOCHKA */ +$config['0400_04ff'][] = array('upper' => 1217, 'status' => 'C', 'lower' => array(1218)); /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1219, 'status' => 'C', 'lower' => array(1220)); /* CYRILLIC CAPITAL LETTER KA WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1221, 'status' => 'C', 'lower' => array(1222)); /* CYRILLIC CAPITAL LETTER EL WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1223, 'status' => 'C', 'lower' => array(1224)); /* CYRILLIC CAPITAL LETTER EN WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1225, 'status' => 'C', 'lower' => array(1226)); /* CYRILLIC CAPITAL LETTER EN WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1227, 'status' => 'C', 'lower' => array(1228)); /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ +$config['0400_04ff'][] = array('upper' => 1229, 'status' => 'C', 'lower' => array(1230)); /* CYRILLIC CAPITAL LETTER EM WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1232, 'status' => 'C', 'lower' => array(1233)); /* CYRILLIC CAPITAL LETTER A WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1234, 'status' => 'C', 'lower' => array(1235)); /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1236, 'status' => 'C', 'lower' => array(1237)); /* CYRILLIC CAPITAL LIGATURE A IE */ +$config['0400_04ff'][] = array('upper' => 1238, 'status' => 'C', 'lower' => array(1239)); /* CYRILLIC CAPITAL LETTER IE WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1240, 'status' => 'C', 'lower' => array(1241)); /* CYRILLIC CAPITAL LETTER SCHWA */ +$config['0400_04ff'][] = array('upper' => 1242, 'status' => 'C', 'lower' => array(1243)); /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1244, 'status' => 'C', 'lower' => array(1245)); /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1246, 'status' => 'C', 'lower' => array(1247)); /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1248, 'status' => 'C', 'lower' => array(1249)); /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ +$config['0400_04ff'][] = array('upper' => 1250, 'status' => 'C', 'lower' => array(1251)); /* CYRILLIC CAPITAL LETTER I WITH MACRON */ +$config['0400_04ff'][] = array('upper' => 1252, 'status' => 'C', 'lower' => array(1253)); /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1254, 'status' => 'C', 'lower' => array(1255)); /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1256, 'status' => 'C', 'lower' => array(1257)); /* CYRILLIC CAPITAL LETTER BARRED O */ +$config['0400_04ff'][] = array('upper' => 1258, 'status' => 'C', 'lower' => array(1259)); /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1260, 'status' => 'C', 'lower' => array(1261)); /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1262, 'status' => 'C', 'lower' => array(1263)); /* CYRILLIC CAPITAL LETTER U WITH MACRON */ +$config['0400_04ff'][] = array('upper' => 1264, 'status' => 'C', 'lower' => array(1265)); /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1266, 'status' => 'C', 'lower' => array(1267)); /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */ +$config['0400_04ff'][] = array('upper' => 1268, 'status' => 'C', 'lower' => array(1269)); /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1270, 'status' => 'C', 'lower' => array(1271)); /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1272, 'status' => 'C', 'lower' => array(1273)); /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1274, 'status' => 'C', 'lower' => array(1275)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */ +$config['0400_04ff'][] = array('upper' => 1276, 'status' => 'C', 'lower' => array(1277)); /* CYRILLIC CAPITAL LETTER HA WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1278, 'status' => 'C', 'lower' => array(1279)); /* CYRILLIC CAPITAL LETTER HA WITH STROKE */ diff --git a/app/Cake/Config/unicode/casefolding/0500_052f.php b/app/Cake/Config/unicode/casefolding/0500_052f.php new file mode 100644 index 00000000..32b5e7cc --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0500_052f.php @@ -0,0 +1,50 @@ + 1280, 'status' => 'C', 'lower' => array(1281)); /* CYRILLIC CAPITAL LETTER KOMI DE */ +$config['0500_052f'][] = array('upper' => 1282, 'status' => 'C', 'lower' => array(1283)); /* CYRILLIC CAPITAL LETTER KOMI DJE */ +$config['0500_052f'][] = array('upper' => 1284, 'status' => 'C', 'lower' => array(1285)); /* CYRILLIC CAPITAL LETTER KOMI ZJE */ +$config['0500_052f'][] = array('upper' => 1286, 'status' => 'C', 'lower' => array(1287)); /* CYRILLIC CAPITAL LETTER KOMI DZJE */ +$config['0500_052f'][] = array('upper' => 1288, 'status' => 'C', 'lower' => array(1289)); /* CYRILLIC CAPITAL LETTER KOMI LJE */ +$config['0500_052f'][] = array('upper' => 1290, 'status' => 'C', 'lower' => array(1291)); /* CYRILLIC CAPITAL LETTER KOMI NJE */ +$config['0500_052f'][] = array('upper' => 1292, 'status' => 'C', 'lower' => array(1293)); /* CYRILLIC CAPITAL LETTER KOMI SJE */ +$config['0500_052f'][] = array('upper' => 1294, 'status' => 'C', 'lower' => array(1295)); /* CYRILLIC CAPITAL LETTER KOMI TJE */ +$config['0500_052f'][] = array('upper' => 1296, 'status' => 'C', 'lower' => array(1297)); /* CYRILLIC CAPITAL LETTER ZE */ +$config['0500_052f'][] = array('upper' => 1298, 'status' => 'C', 'lower' => array(1299)); /* CYRILLIC CAPITAL LETTER El with hook */ diff --git a/app/Cake/Config/unicode/casefolding/0530_058f.php b/app/Cake/Config/unicode/casefolding/0530_058f.php new file mode 100644 index 00000000..713968e8 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/0530_058f.php @@ -0,0 +1,78 @@ + 1329, 'status' => 'C', 'lower' => array(1377)); /* ARMENIAN CAPITAL LETTER AYB */ +$config['0530_058f'][] = array('upper' => 1330, 'status' => 'C', 'lower' => array(1378)); /* ARMENIAN CAPITAL LETTER BEN */ +$config['0530_058f'][] = array('upper' => 1331, 'status' => 'C', 'lower' => array(1379)); /* ARMENIAN CAPITAL LETTER GIM */ +$config['0530_058f'][] = array('upper' => 1332, 'status' => 'C', 'lower' => array(1380)); /* ARMENIAN CAPITAL LETTER DA */ +$config['0530_058f'][] = array('upper' => 1333, 'status' => 'C', 'lower' => array(1381)); /* ARMENIAN CAPITAL LETTER ECH */ +$config['0530_058f'][] = array('upper' => 1334, 'status' => 'C', 'lower' => array(1382)); /* ARMENIAN CAPITAL LETTER ZA */ +$config['0530_058f'][] = array('upper' => 1335, 'status' => 'C', 'lower' => array(1383)); /* ARMENIAN CAPITAL LETTER EH */ +$config['0530_058f'][] = array('upper' => 1336, 'status' => 'C', 'lower' => array(1384)); /* ARMENIAN CAPITAL LETTER ET */ +$config['0530_058f'][] = array('upper' => 1337, 'status' => 'C', 'lower' => array(1385)); /* ARMENIAN CAPITAL LETTER TO */ +$config['0530_058f'][] = array('upper' => 1338, 'status' => 'C', 'lower' => array(1386)); /* ARMENIAN CAPITAL LETTER ZHE */ +$config['0530_058f'][] = array('upper' => 1339, 'status' => 'C', 'lower' => array(1387)); /* ARMENIAN CAPITAL LETTER INI */ +$config['0530_058f'][] = array('upper' => 1340, 'status' => 'C', 'lower' => array(1388)); /* ARMENIAN CAPITAL LETTER LIWN */ +$config['0530_058f'][] = array('upper' => 1341, 'status' => 'C', 'lower' => array(1389)); /* ARMENIAN CAPITAL LETTER XEH */ +$config['0530_058f'][] = array('upper' => 1342, 'status' => 'C', 'lower' => array(1390)); /* ARMENIAN CAPITAL LETTER CA */ +$config['0530_058f'][] = array('upper' => 1343, 'status' => 'C', 'lower' => array(1391)); /* ARMENIAN CAPITAL LETTER KEN */ +$config['0530_058f'][] = array('upper' => 1344, 'status' => 'C', 'lower' => array(1392)); /* ARMENIAN CAPITAL LETTER HO */ +$config['0530_058f'][] = array('upper' => 1345, 'status' => 'C', 'lower' => array(1393)); /* ARMENIAN CAPITAL LETTER JA */ +$config['0530_058f'][] = array('upper' => 1346, 'status' => 'C', 'lower' => array(1394)); /* ARMENIAN CAPITAL LETTER GHAD */ +$config['0530_058f'][] = array('upper' => 1347, 'status' => 'C', 'lower' => array(1395)); /* ARMENIAN CAPITAL LETTER CHEH */ +$config['0530_058f'][] = array('upper' => 1348, 'status' => 'C', 'lower' => array(1396)); /* ARMENIAN CAPITAL LETTER MEN */ +$config['0530_058f'][] = array('upper' => 1349, 'status' => 'C', 'lower' => array(1397)); /* ARMENIAN CAPITAL LETTER YI */ +$config['0530_058f'][] = array('upper' => 1350, 'status' => 'C', 'lower' => array(1398)); /* ARMENIAN CAPITAL LETTER NOW */ +$config['0530_058f'][] = array('upper' => 1351, 'status' => 'C', 'lower' => array(1399)); /* ARMENIAN CAPITAL LETTER SHA */ +$config['0530_058f'][] = array('upper' => 1352, 'status' => 'C', 'lower' => array(1400)); /* ARMENIAN CAPITAL LETTER VO */ +$config['0530_058f'][] = array('upper' => 1353, 'status' => 'C', 'lower' => array(1401)); /* ARMENIAN CAPITAL LETTER CHA */ +$config['0530_058f'][] = array('upper' => 1354, 'status' => 'C', 'lower' => array(1402)); /* ARMENIAN CAPITAL LETTER PEH */ +$config['0530_058f'][] = array('upper' => 1355, 'status' => 'C', 'lower' => array(1403)); /* ARMENIAN CAPITAL LETTER JHEH */ +$config['0530_058f'][] = array('upper' => 1356, 'status' => 'C', 'lower' => array(1404)); /* ARMENIAN CAPITAL LETTER RA */ +$config['0530_058f'][] = array('upper' => 1357, 'status' => 'C', 'lower' => array(1405)); /* ARMENIAN CAPITAL LETTER SEH */ +$config['0530_058f'][] = array('upper' => 1358, 'status' => 'C', 'lower' => array(1406)); /* ARMENIAN CAPITAL LETTER VEW */ +$config['0530_058f'][] = array('upper' => 1359, 'status' => 'C', 'lower' => array(1407)); /* ARMENIAN CAPITAL LETTER TIWN */ +$config['0530_058f'][] = array('upper' => 1360, 'status' => 'C', 'lower' => array(1408)); /* ARMENIAN CAPITAL LETTER REH */ +$config['0530_058f'][] = array('upper' => 1361, 'status' => 'C', 'lower' => array(1409)); /* ARMENIAN CAPITAL LETTER CO */ +$config['0530_058f'][] = array('upper' => 1362, 'status' => 'C', 'lower' => array(1410)); /* ARMENIAN CAPITAL LETTER YIWN */ +$config['0530_058f'][] = array('upper' => 1363, 'status' => 'C', 'lower' => array(1411)); /* ARMENIAN CAPITAL LETTER PIWR */ +$config['0530_058f'][] = array('upper' => 1364, 'status' => 'C', 'lower' => array(1412)); /* ARMENIAN CAPITAL LETTER KEH */ +$config['0530_058f'][] = array('upper' => 1365, 'status' => 'C', 'lower' => array(1413)); /* ARMENIAN CAPITAL LETTER OH */ +$config['0530_058f'][] = array('upper' => 1366, 'status' => 'C', 'lower' => array(1414)); /* ARMENIAN CAPITAL LETTER FEH */ diff --git a/app/Cake/Config/unicode/casefolding/1e00_1eff.php b/app/Cake/Config/unicode/casefolding/1e00_1eff.php new file mode 100644 index 00000000..f0dc2183 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/1e00_1eff.php @@ -0,0 +1,168 @@ + 7680, 'status' => 'C', 'lower' => array(7681)); /* LATIN CAPITAL LETTER A WITH RING BELOW */ +$config['1e00_1eff'][] = array('upper' => 7682, 'status' => 'C', 'lower' => array(7683)); /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7684, 'status' => 'C', 'lower' => array(7685)); /* LATIN CAPITAL LETTER B WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7686, 'status' => 'C', 'lower' => array(7687)); /* LATIN CAPITAL LETTER B WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7688, 'status' => 'C', 'lower' => array(7689)); /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7690, 'status' => 'C', 'lower' => array(7691)); /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7692, 'status' => 'C', 'lower' => array(7693)); /* LATIN CAPITAL LETTER D WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7694, 'status' => 'C', 'lower' => array(7695)); /* LATIN CAPITAL LETTER D WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7696, 'status' => 'C', 'lower' => array(7697)); /* LATIN CAPITAL LETTER D WITH CEDILLA */ +$config['1e00_1eff'][] = array('upper' => 7698, 'status' => 'C', 'lower' => array(7699)); /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7700, 'status' => 'C', 'lower' => array(7701)); /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7702, 'status' => 'C', 'lower' => array(7703)); /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7704, 'status' => 'C', 'lower' => array(7705)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7706, 'status' => 'C', 'lower' => array(7707)); /* LATIN CAPITAL LETTER E WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7708, 'status' => 'C', 'lower' => array(7709)); /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */ +$config['1e00_1eff'][] = array('upper' => 7710, 'status' => 'C', 'lower' => array(7711)); /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7712, 'status' => 'C', 'lower' => array(7713)); /* LATIN CAPITAL LETTER G WITH MACRON */ +$config['1e00_1eff'][] = array('upper' => 7714, 'status' => 'C', 'lower' => array(7715)); /* LATIN CAPITAL LETTER H WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7716, 'status' => 'C', 'lower' => array(7717)); /* LATIN CAPITAL LETTER H WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7718, 'status' => 'C', 'lower' => array(7719)); /* LATIN CAPITAL LETTER H WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7720, 'status' => 'C', 'lower' => array(7721)); /* LATIN CAPITAL LETTER H WITH CEDILLA */ +$config['1e00_1eff'][] = array('upper' => 7722, 'status' => 'C', 'lower' => array(7723)); /* LATIN CAPITAL LETTER H WITH BREVE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7724, 'status' => 'C', 'lower' => array(7725)); /* LATIN CAPITAL LETTER I WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7726, 'status' => 'C', 'lower' => array(7727)); /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7728, 'status' => 'C', 'lower' => array(7729)); /* LATIN CAPITAL LETTER K WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7730, 'status' => 'C', 'lower' => array(7731)); /* LATIN CAPITAL LETTER K WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7732, 'status' => 'C', 'lower' => array(7733)); /* LATIN CAPITAL LETTER K WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7734, 'status' => 'C', 'lower' => array(7735)); /* LATIN CAPITAL LETTER L WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7736, 'status' => 'C', 'lower' => array(7737)); /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */ +$config['1e00_1eff'][] = array('upper' => 7738, 'status' => 'C', 'lower' => array(7739)); /* LATIN CAPITAL LETTER L WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7740, 'status' => 'C', 'lower' => array(7741)); /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7742, 'status' => 'C', 'lower' => array(7743)); /* LATIN CAPITAL LETTER M WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7744, 'status' => 'C', 'lower' => array(7745)); /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7746, 'status' => 'C', 'lower' => array(7747)); /* LATIN CAPITAL LETTER M WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7748, 'status' => 'C', 'lower' => array(7749)); /* LATIN CAPITAL LETTER N WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7750, 'status' => 'C', 'lower' => array(7751)); /* LATIN CAPITAL LETTER N WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7752, 'status' => 'C', 'lower' => array(7753)); /* LATIN CAPITAL LETTER N WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7754, 'status' => 'C', 'lower' => array(7755)); /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7756, 'status' => 'C', 'lower' => array(7757)); /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7758, 'status' => 'C', 'lower' => array(7759)); /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7760, 'status' => 'C', 'lower' => array(7761)); /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7762, 'status' => 'C', 'lower' => array(7763)); /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7764, 'status' => 'C', 'lower' => array(7765)); /* LATIN CAPITAL LETTER P WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7766, 'status' => 'C', 'lower' => array(7767)); /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7768, 'status' => 'C', 'lower' => array(7769)); /* LATIN CAPITAL LETTER R WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7770, 'status' => 'C', 'lower' => array(7771)); /* LATIN CAPITAL LETTER R WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7772, 'status' => 'C', 'lower' => array(7773)); /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */ +$config['1e00_1eff'][] = array('upper' => 7774, 'status' => 'C', 'lower' => array(7775)); /* LATIN CAPITAL LETTER R WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7776, 'status' => 'C', 'lower' => array(7777)); /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7778, 'status' => 'C', 'lower' => array(7779)); /* LATIN CAPITAL LETTER S WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7780, 'status' => 'C', 'lower' => array(7781)); /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7782, 'status' => 'C', 'lower' => array(7783)); /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7784, 'status' => 'C', 'lower' => array(7785)); /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7786, 'status' => 'C', 'lower' => array(7787)); /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7788, 'status' => 'C', 'lower' => array(7789)); /* LATIN CAPITAL LETTER T WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7790, 'status' => 'C', 'lower' => array(7791)); /* LATIN CAPITAL LETTER T WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7792, 'status' => 'C', 'lower' => array(7793)); /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7794, 'status' => 'C', 'lower' => array(7795)); /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */ +$config['1e00_1eff'][] = array('upper' => 7796, 'status' => 'C', 'lower' => array(7797)); /* LATIN CAPITAL LETTER U WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7798, 'status' => 'C', 'lower' => array(7799)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7800, 'status' => 'C', 'lower' => array(7801)); /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7802, 'status' => 'C', 'lower' => array(7803)); /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7804, 'status' => 'C', 'lower' => array(7805)); /* LATIN CAPITAL LETTER V WITH TILDE */ +$config['1e00_1eff'][] = array('upper' => 7806, 'status' => 'C', 'lower' => array(7807)); /* LATIN CAPITAL LETTER V WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7808, 'status' => 'C', 'lower' => array(7809)); /* LATIN CAPITAL LETTER W WITH GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7810, 'status' => 'C', 'lower' => array(7811)); /* LATIN CAPITAL LETTER W WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7812, 'status' => 'C', 'lower' => array(7813)); /* LATIN CAPITAL LETTER W WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7814, 'status' => 'C', 'lower' => array(7815)); /* LATIN CAPITAL LETTER W WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7816, 'status' => 'C', 'lower' => array(7817)); /* LATIN CAPITAL LETTER W WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7818, 'status' => 'C', 'lower' => array(7819)); /* LATIN CAPITAL LETTER X WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7820, 'status' => 'C', 'lower' => array(7821)); /* LATIN CAPITAL LETTER X WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7822, 'status' => 'C', 'lower' => array(7823)); /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7824, 'status' => 'C', 'lower' => array(7825)); /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ +$config['1e00_1eff'][] = array('upper' => 7826, 'status' => 'C', 'lower' => array(7827)); /* LATIN CAPITAL LETTER Z WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7828, 'status' => 'C', 'lower' => array(7829)); /* LATIN CAPITAL LETTER Z WITH LINE BELOW */ + +//$config['1e00_1eff'][] = array('upper' => 7830, 'status' => 'F', 'lower' => array(104, 817)); /* LATIN SMALL LETTER H WITH LINE BELOW */ +//$config['1e00_1eff'][] = array('upper' => 7831, 'status' => 'F', 'lower' => array(116, 776)); /* LATIN SMALL LETTER T WITH DIAERESIS */ +//$config['1e00_1eff'][] = array('upper' => 7832, 'status' => 'F', 'lower' => array(119, 778)); /* LATIN SMALL LETTER W WITH RING ABOVE */ +//$config['1e00_1eff'][] = array('upper' => 7833, 'status' => 'F', 'lower' => array(121, 778)); /* LATIN SMALL LETTER Y WITH RING ABOVE */ +//$config['1e00_1eff'][] = array('upper' => 7834, 'status' => 'F', 'lower' => array(97, 702)); /* LATIN SMALL LETTER A WITH RIGHT HALF RING */ +//$config['1e00_1eff'][] = array('upper' => 7835, 'status' => 'C', 'lower' => array(7777)); /* LATIN SMALL LETTER LONG S WITH DOT ABOVE */ + +$config['1e00_1eff'][] = array('upper' => 7840, 'status' => 'C', 'lower' => array(7841)); /* LATIN CAPITAL LETTER A WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7842, 'status' => 'C', 'lower' => array(7843)); /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7844, 'status' => 'C', 'lower' => array(7845)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7846, 'status' => 'C', 'lower' => array(7847)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7848, 'status' => 'C', 'lower' => array(7849)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7850, 'status' => 'C', 'lower' => array(7851)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7852, 'status' => 'C', 'lower' => array(7853)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7854, 'status' => 'C', 'lower' => array(7855)); /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7856, 'status' => 'C', 'lower' => array(7857)); /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7858, 'status' => 'C', 'lower' => array(7859)); /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7860, 'status' => 'C', 'lower' => array(7861)); /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7862, 'status' => 'C', 'lower' => array(7863)); /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7864, 'status' => 'C', 'lower' => array(7865)); /* LATIN CAPITAL LETTER E WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7866, 'status' => 'C', 'lower' => array(7867)); /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7868, 'status' => 'C', 'lower' => array(7869)); /* LATIN CAPITAL LETTER E WITH TILDE */ +$config['1e00_1eff'][] = array('upper' => 7870, 'status' => 'C', 'lower' => array(7871)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7872, 'status' => 'C', 'lower' => array(7873)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7874, 'status' => 'C', 'lower' => array(7875)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7876, 'status' => 'C', 'lower' => array(7877)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7878, 'status' => 'C', 'lower' => array(7879)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7880, 'status' => 'C', 'lower' => array(7881)); /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7882, 'status' => 'C', 'lower' => array(7883)); /* LATIN CAPITAL LETTER I WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7884, 'status' => 'C', 'lower' => array(7885)); /* LATIN CAPITAL LETTER O WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7886, 'status' => 'C', 'lower' => array(7887)); /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7888, 'status' => 'C', 'lower' => array(7889)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7890, 'status' => 'C', 'lower' => array(7891)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7892, 'status' => 'C', 'lower' => array(7893)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7894, 'status' => 'C', 'lower' => array(7895)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7896, 'status' => 'C', 'lower' => array(7897)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7898, 'status' => 'C', 'lower' => array(7899)); /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7900, 'status' => 'C', 'lower' => array(7901)); /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7902, 'status' => 'C', 'lower' => array(7903)); /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7904, 'status' => 'C', 'lower' => array(7905)); /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7906, 'status' => 'C', 'lower' => array(7907)); /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7908, 'status' => 'C', 'lower' => array(7909)); /* LATIN CAPITAL LETTER U WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7910, 'status' => 'C', 'lower' => array(7911)); /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7912, 'status' => 'C', 'lower' => array(7913)); /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7914, 'status' => 'C', 'lower' => array(7915)); /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7916, 'status' => 'C', 'lower' => array(7917)); /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7918, 'status' => 'C', 'lower' => array(7919)); /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7920, 'status' => 'C', 'lower' => array(7921)); /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7922, 'status' => 'C', 'lower' => array(7923)); /* LATIN CAPITAL LETTER Y WITH GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7924, 'status' => 'C', 'lower' => array(7925)); /* LATIN CAPITAL LETTER Y WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7926, 'status' => 'C', 'lower' => array(7927)); /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7928, 'status' => 'C', 'lower' => array(7929)); /* LATIN CAPITAL LETTER Y WITH TILDE */ diff --git a/app/Cake/Config/unicode/casefolding/1f00_1fff.php b/app/Cake/Config/unicode/casefolding/1f00_1fff.php new file mode 100644 index 00000000..b68b2cfb --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/1f00_1fff.php @@ -0,0 +1,216 @@ + 7944, 'status' => 'C', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7945, 'status' => 'C', 'lower' => array(7937)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7946, 'status' => 'C', 'lower' => array(7938)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7947, 'status' => 'C', 'lower' => array(7939)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7948, 'status' => 'C', 'lower' => array(7940)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7949, 'status' => 'C', 'lower' => array(7941)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7950, 'status' => 'C', 'lower' => array(7942)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7951, 'status' => 'C', 'lower' => array(7943)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7960, 'status' => 'C', 'lower' => array(7952)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7961, 'status' => 'C', 'lower' => array(7953)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7962, 'status' => 'C', 'lower' => array(7954)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7963, 'status' => 'C', 'lower' => array(7955)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7964, 'status' => 'C', 'lower' => array(7956)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7965, 'status' => 'C', 'lower' => array(7957)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7976, 'status' => 'C', 'lower' => array(7968)); /* GREEK CAPITAL LETTER ETA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7977, 'status' => 'C', 'lower' => array(7969)); /* GREEK CAPITAL LETTER ETA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7978, 'status' => 'C', 'lower' => array(7970)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7979, 'status' => 'C', 'lower' => array(7971)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7980, 'status' => 'C', 'lower' => array(7972)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7981, 'status' => 'C', 'lower' => array(7973)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7982, 'status' => 'C', 'lower' => array(7974)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7983, 'status' => 'C', 'lower' => array(7975)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7992, 'status' => 'C', 'lower' => array(7984)); /* GREEK CAPITAL LETTER IOTA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7993, 'status' => 'C', 'lower' => array(7985)); /* GREEK CAPITAL LETTER IOTA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7994, 'status' => 'C', 'lower' => array(7986)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7995, 'status' => 'C', 'lower' => array(7987)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7996, 'status' => 'C', 'lower' => array(7988)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7997, 'status' => 'C', 'lower' => array(7989)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7998, 'status' => 'C', 'lower' => array(7990)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7999, 'status' => 'C', 'lower' => array(7991)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8008, 'status' => 'C', 'lower' => array(8000)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8009, 'status' => 'C', 'lower' => array(8001)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8010, 'status' => 'C', 'lower' => array(8002)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8011, 'status' => 'C', 'lower' => array(8003)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8012, 'status' => 'C', 'lower' => array(8004)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8013, 'status' => 'C', 'lower' => array(8005)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8016, 'status' => 'F', 'lower' => array(965, 787)); /* GREEK SMALL LETTER UPSILON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8018, 'status' => 'F', 'lower' => array(965, 787, 768)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8020, 'status' => 'F', 'lower' => array(965, 787, 769)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8022, 'status' => 'F', 'lower' => array(965, 787, 834)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8025, 'status' => 'C', 'lower' => array(8017)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8027, 'status' => 'C', 'lower' => array(8019)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8029, 'status' => 'C', 'lower' => array(8021)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8031, 'status' => 'C', 'lower' => array(8023)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8040, 'status' => 'C', 'lower' => array(8032)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8041, 'status' => 'C', 'lower' => array(8033)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8042, 'status' => 'C', 'lower' => array(8034)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8043, 'status' => 'C', 'lower' => array(8035)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8044, 'status' => 'C', 'lower' => array(8036)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8045, 'status' => 'C', 'lower' => array(8037)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8046, 'status' => 'C', 'lower' => array(8038)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8047, 'status' => 'C', 'lower' => array(8039)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8064, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8065, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8066, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8067, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8068, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8069, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8070, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8071, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'S', 'lower' => array(8064)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'S', 'lower' => array(8065)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'S', 'lower' => array(8066)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'S', 'lower' => array(8067)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'S', 'lower' => array(8068)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'S', 'lower' => array(8069)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'S', 'lower' => array(8070)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'S', 'lower' => array(8071)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8080, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8081, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8082, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8083, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8084, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8085, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8086, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8087, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'S', 'lower' => array(8080)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'S', 'lower' => array(8081)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'S', 'lower' => array(8082)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'S', 'lower' => array(8083)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'S', 'lower' => array(8084)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'S', 'lower' => array(8085)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'S', 'lower' => array(8086)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'S', 'lower' => array(8087)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8096, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8097, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8098, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8099, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8100, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8101, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8102, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8103, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'S', 'lower' => array(8096)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'S', 'lower' => array(8097)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'S', 'lower' => array(8098)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'S', 'lower' => array(8099)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'S', 'lower' => array(8100)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'S', 'lower' => array(8101)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'S', 'lower' => array(8102)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'S', 'lower' => array(8103)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8114, 'status' => 'F', 'lower' => array(8048, 953)); /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8115, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8116, 'status' => 'F', 'lower' => array(940, 953)); /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8118, 'status' => 'F', 'lower' => array(945, 834)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8119, 'status' => 'F', 'lower' => array(945, 834, 953)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8120, 'status' => 'C', 'lower' => array(8112)); /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8121, 'status' => 'C', 'lower' => array(8113)); /* GREEK CAPITAL LETTER ALPHA WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8122, 'status' => 'C', 'lower' => array(8048)); /* GREEK CAPITAL LETTER ALPHA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8123, 'status' => 'C', 'lower' => array(8049)); /* GREEK CAPITAL LETTER ALPHA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'S', 'lower' => array(8115)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8126, 'status' => 'C', 'lower' => array(953)); /* GREEK PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8130, 'status' => 'F', 'lower' => array(8052, 953)); /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8131, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8132, 'status' => 'F', 'lower' => array(942, 953)); /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8134, 'status' => 'F', 'lower' => array(951, 834)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8135, 'status' => 'F', 'lower' => array(951, 834, 953)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8136, 'status' => 'C', 'lower' => array(8050)); /* GREEK CAPITAL LETTER EPSILON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8137, 'status' => 'C', 'lower' => array(8051)); /* GREEK CAPITAL LETTER EPSILON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8138, 'status' => 'C', 'lower' => array(8052)); /* GREEK CAPITAL LETTER ETA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8139, 'status' => 'C', 'lower' => array(8053)); /* GREEK CAPITAL LETTER ETA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'S', 'lower' => array(8131)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8146, 'status' => 'F', 'lower' => array(953, 776, 768)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8147, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8150, 'status' => 'F', 'lower' => array(953, 834)); /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8151, 'status' => 'F', 'lower' => array(953, 776, 834)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8152, 'status' => 'C', 'lower' => array(8144)); /* GREEK CAPITAL LETTER IOTA WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8153, 'status' => 'C', 'lower' => array(8145)); /* GREEK CAPITAL LETTER IOTA WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8154, 'status' => 'C', 'lower' => array(8054)); /* GREEK CAPITAL LETTER IOTA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8155, 'status' => 'C', 'lower' => array(8055)); /* GREEK CAPITAL LETTER IOTA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8162, 'status' => 'F', 'lower' => array(965, 776, 768)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8163, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8164, 'status' => 'F', 'lower' => array(961, 787)); /* GREEK SMALL LETTER RHO WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8166, 'status' => 'F', 'lower' => array(965, 834)); /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8167, 'status' => 'F', 'lower' => array(965, 776, 834)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8168, 'status' => 'C', 'lower' => array(8160)); /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8169, 'status' => 'C', 'lower' => array(8161)); /* GREEK CAPITAL LETTER UPSILON WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8170, 'status' => 'C', 'lower' => array(8058)); /* GREEK CAPITAL LETTER UPSILON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8171, 'status' => 'C', 'lower' => array(8059)); /* GREEK CAPITAL LETTER UPSILON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8172, 'status' => 'C', 'lower' => array(8165)); /* GREEK CAPITAL LETTER RHO WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8178, 'status' => 'F', 'lower' => array(8060, 953)); /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8179, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8180, 'status' => 'F', 'lower' => array(974, 953)); /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8182, 'status' => 'F', 'lower' => array(969, 834)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8183, 'status' => 'F', 'lower' => array(969, 834, 953)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8184, 'status' => 'C', 'lower' => array(8056)); /* GREEK CAPITAL LETTER OMICRON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8185, 'status' => 'C', 'lower' => array(8057)); /* GREEK CAPITAL LETTER OMICRON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8186, 'status' => 'C', 'lower' => array(8060)); /* GREEK CAPITAL LETTER OMEGA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8187, 'status' => 'C', 'lower' => array(8061)); /* GREEK CAPITAL LETTER OMEGA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'S', 'lower' => array(8179)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */ diff --git a/app/Cake/Config/unicode/casefolding/2100_214f.php b/app/Cake/Config/unicode/casefolding/2100_214f.php new file mode 100644 index 00000000..6f7ab30d --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2100_214f.php @@ -0,0 +1,44 @@ + 8486, 'status' => 'C', 'lower' => array(969)); /* OHM SIGN */ +$config['2100_214f'][] = array('upper' => 8490, 'status' => 'C', 'lower' => array(107)); /* KELVIN SIGN */ +$config['2100_214f'][] = array('upper' => 8491, 'status' => 'C', 'lower' => array(229)); /* ANGSTROM SIGN */ +$config['2100_214f'][] = array('upper' => 8498, 'status' => 'C', 'lower' => array(8526)); /* TURNED CAPITAL F */ diff --git a/app/Cake/Config/unicode/casefolding/2150_218f.php b/app/Cake/Config/unicode/casefolding/2150_218f.php new file mode 100644 index 00000000..875e1868 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2150_218f.php @@ -0,0 +1,57 @@ + 8544, 'status' => 'C', 'lower' => array(8560)); /* ROMAN NUMERAL ONE */ +$config['2150_218f'][] = array('upper' => 8545, 'status' => 'C', 'lower' => array(8561)); /* ROMAN NUMERAL TWO */ +$config['2150_218f'][] = array('upper' => 8546, 'status' => 'C', 'lower' => array(8562)); /* ROMAN NUMERAL THREE */ +$config['2150_218f'][] = array('upper' => 8547, 'status' => 'C', 'lower' => array(8563)); /* ROMAN NUMERAL FOUR */ +$config['2150_218f'][] = array('upper' => 8548, 'status' => 'C', 'lower' => array(8564)); /* ROMAN NUMERAL FIVE */ +$config['2150_218f'][] = array('upper' => 8549, 'status' => 'C', 'lower' => array(8565)); /* ROMAN NUMERAL SIX */ +$config['2150_218f'][] = array('upper' => 8550, 'status' => 'C', 'lower' => array(8566)); /* ROMAN NUMERAL SEVEN */ +$config['2150_218f'][] = array('upper' => 8551, 'status' => 'C', 'lower' => array(8567)); /* ROMAN NUMERAL EIGHT */ +$config['2150_218f'][] = array('upper' => 8552, 'status' => 'C', 'lower' => array(8568)); /* ROMAN NUMERAL NINE */ +$config['2150_218f'][] = array('upper' => 8553, 'status' => 'C', 'lower' => array(8569)); /* ROMAN NUMERAL TEN */ +$config['2150_218f'][] = array('upper' => 8554, 'status' => 'C', 'lower' => array(8570)); /* ROMAN NUMERAL ELEVEN */ +$config['2150_218f'][] = array('upper' => 8555, 'status' => 'C', 'lower' => array(8571)); /* ROMAN NUMERAL TWELVE */ +$config['2150_218f'][] = array('upper' => 8556, 'status' => 'C', 'lower' => array(8572)); /* ROMAN NUMERAL FIFTY */ +$config['2150_218f'][] = array('upper' => 8557, 'status' => 'C', 'lower' => array(8573)); /* ROMAN NUMERAL ONE HUNDRED */ +$config['2150_218f'][] = array('upper' => 8558, 'status' => 'C', 'lower' => array(8574)); /* ROMAN NUMERAL FIVE HUNDRED */ +$config['2150_218f'][] = array('upper' => 8559, 'status' => 'C', 'lower' => array(8575)); /* ROMAN NUMERAL ONE THOUSAND */ +$config['2150_218f'][] = array('upper' => 8579, 'status' => 'C', 'lower' => array(8580)); /* ROMAN NUMERAL REVERSED ONE HUNDRED */ diff --git a/app/Cake/Config/unicode/casefolding/2460_24ff.php b/app/Cake/Config/unicode/casefolding/2460_24ff.php new file mode 100644 index 00000000..a1abe9b5 --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2460_24ff.php @@ -0,0 +1,66 @@ + 9398, 'status' => 'C', 'lower' => array(9424)); /* CIRCLED LATIN CAPITAL LETTER A */ +$config['2460_24ff'][] = array('upper' => 9399, 'status' => 'C', 'lower' => array(9425)); /* CIRCLED LATIN CAPITAL LETTER B */ +$config['2460_24ff'][] = array('upper' => 9400, 'status' => 'C', 'lower' => array(9426)); /* CIRCLED LATIN CAPITAL LETTER C */ +$config['2460_24ff'][] = array('upper' => 9401, 'status' => 'C', 'lower' => array(9427)); /* CIRCLED LATIN CAPITAL LETTER D */ +$config['2460_24ff'][] = array('upper' => 9402, 'status' => 'C', 'lower' => array(9428)); /* CIRCLED LATIN CAPITAL LETTER E */ +$config['2460_24ff'][] = array('upper' => 9403, 'status' => 'C', 'lower' => array(9429)); /* CIRCLED LATIN CAPITAL LETTER F */ +$config['2460_24ff'][] = array('upper' => 9404, 'status' => 'C', 'lower' => array(9430)); /* CIRCLED LATIN CAPITAL LETTER G */ +$config['2460_24ff'][] = array('upper' => 9405, 'status' => 'C', 'lower' => array(9431)); /* CIRCLED LATIN CAPITAL LETTER H */ +$config['2460_24ff'][] = array('upper' => 9406, 'status' => 'C', 'lower' => array(9432)); /* CIRCLED LATIN CAPITAL LETTER I */ +$config['2460_24ff'][] = array('upper' => 9407, 'status' => 'C', 'lower' => array(9433)); /* CIRCLED LATIN CAPITAL LETTER J */ +$config['2460_24ff'][] = array('upper' => 9408, 'status' => 'C', 'lower' => array(9434)); /* CIRCLED LATIN CAPITAL LETTER K */ +$config['2460_24ff'][] = array('upper' => 9409, 'status' => 'C', 'lower' => array(9435)); /* CIRCLED LATIN CAPITAL LETTER L */ +$config['2460_24ff'][] = array('upper' => 9410, 'status' => 'C', 'lower' => array(9436)); /* CIRCLED LATIN CAPITAL LETTER M */ +$config['2460_24ff'][] = array('upper' => 9411, 'status' => 'C', 'lower' => array(9437)); /* CIRCLED LATIN CAPITAL LETTER N */ +$config['2460_24ff'][] = array('upper' => 9412, 'status' => 'C', 'lower' => array(9438)); /* CIRCLED LATIN CAPITAL LETTER O */ +$config['2460_24ff'][] = array('upper' => 9413, 'status' => 'C', 'lower' => array(9439)); /* CIRCLED LATIN CAPITAL LETTER P */ +$config['2460_24ff'][] = array('upper' => 9414, 'status' => 'C', 'lower' => array(9440)); /* CIRCLED LATIN CAPITAL LETTER Q */ +$config['2460_24ff'][] = array('upper' => 9415, 'status' => 'C', 'lower' => array(9441)); /* CIRCLED LATIN CAPITAL LETTER R */ +$config['2460_24ff'][] = array('upper' => 9416, 'status' => 'C', 'lower' => array(9442)); /* CIRCLED LATIN CAPITAL LETTER S */ +$config['2460_24ff'][] = array('upper' => 9417, 'status' => 'C', 'lower' => array(9443)); /* CIRCLED LATIN CAPITAL LETTER T */ +$config['2460_24ff'][] = array('upper' => 9418, 'status' => 'C', 'lower' => array(9444)); /* CIRCLED LATIN CAPITAL LETTER U */ +$config['2460_24ff'][] = array('upper' => 9419, 'status' => 'C', 'lower' => array(9445)); /* CIRCLED LATIN CAPITAL LETTER V */ +$config['2460_24ff'][] = array('upper' => 9420, 'status' => 'C', 'lower' => array(9446)); /* CIRCLED LATIN CAPITAL LETTER W */ +$config['2460_24ff'][] = array('upper' => 9421, 'status' => 'C', 'lower' => array(9447)); /* CIRCLED LATIN CAPITAL LETTER X */ +$config['2460_24ff'][] = array('upper' => 9422, 'status' => 'C', 'lower' => array(9448)); /* CIRCLED LATIN CAPITAL LETTER Y */ +$config['2460_24ff'][] = array('upper' => 9423, 'status' => 'C', 'lower' => array(9449)); /* CIRCLED LATIN CAPITAL LETTER Z */ diff --git a/app/Cake/Config/unicode/casefolding/2c00_2c5f.php b/app/Cake/Config/unicode/casefolding/2c00_2c5f.php new file mode 100644 index 00000000..61608f3e --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2c00_2c5f.php @@ -0,0 +1,87 @@ + 11264, 'status' => 'C', 'lower' => array(11312)); /* GLAGOLITIC CAPITAL LETTER AZU */ +$config['2c00_2c5f'][] = array('upper' => 11265, 'status' => 'C', 'lower' => array(11313)); /* GLAGOLITIC CAPITAL LETTER BUKY */ +$config['2c00_2c5f'][] = array('upper' => 11266, 'status' => 'C', 'lower' => array(11314)); /* GLAGOLITIC CAPITAL LETTER VEDE */ +$config['2c00_2c5f'][] = array('upper' => 11267, 'status' => 'C', 'lower' => array(11315)); /* GLAGOLITIC CAPITAL LETTER GLAGOLI */ +$config['2c00_2c5f'][] = array('upper' => 11268, 'status' => 'C', 'lower' => array(11316)); /* GLAGOLITIC CAPITAL LETTER DOBRO */ +$config['2c00_2c5f'][] = array('upper' => 11269, 'status' => 'C', 'lower' => array(11317)); /* GLAGOLITIC CAPITAL LETTER YESTU */ +$config['2c00_2c5f'][] = array('upper' => 11270, 'status' => 'C', 'lower' => array(11318)); /* GLAGOLITIC CAPITAL LETTER ZHIVETE */ +$config['2c00_2c5f'][] = array('upper' => 11271, 'status' => 'C', 'lower' => array(11319)); /* GLAGOLITIC CAPITAL LETTER DZELO */ +$config['2c00_2c5f'][] = array('upper' => 11272, 'status' => 'C', 'lower' => array(11320)); /* GLAGOLITIC CAPITAL LETTER ZEMLJA */ +$config['2c00_2c5f'][] = array('upper' => 11273, 'status' => 'C', 'lower' => array(11321)); /* GLAGOLITIC CAPITAL LETTER IZHE */ +$config['2c00_2c5f'][] = array('upper' => 11274, 'status' => 'C', 'lower' => array(11322)); /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ +$config['2c00_2c5f'][] = array('upper' => 11275, 'status' => 'C', 'lower' => array(11323)); /* GLAGOLITIC CAPITAL LETTER I */ +$config['2c00_2c5f'][] = array('upper' => 11276, 'status' => 'C', 'lower' => array(11324)); /* GLAGOLITIC CAPITAL LETTER DJERVI */ +$config['2c00_2c5f'][] = array('upper' => 11277, 'status' => 'C', 'lower' => array(11325)); /* GLAGOLITIC CAPITAL LETTER KAKO */ +$config['2c00_2c5f'][] = array('upper' => 11278, 'status' => 'C', 'lower' => array(11326)); /* GLAGOLITIC CAPITAL LETTER LJUDIJE */ +$config['2c00_2c5f'][] = array('upper' => 11279, 'status' => 'C', 'lower' => array(11327)); /* GLAGOLITIC CAPITAL LETTER MYSLITE */ +$config['2c00_2c5f'][] = array('upper' => 11280, 'status' => 'C', 'lower' => array(11328)); /* GLAGOLITIC CAPITAL LETTER NASHI */ +$config['2c00_2c5f'][] = array('upper' => 11281, 'status' => 'C', 'lower' => array(11329)); /* GLAGOLITIC CAPITAL LETTER ONU */ +$config['2c00_2c5f'][] = array('upper' => 11282, 'status' => 'C', 'lower' => array(11330)); /* GLAGOLITIC CAPITAL LETTER POKOJI */ +$config['2c00_2c5f'][] = array('upper' => 11283, 'status' => 'C', 'lower' => array(11331)); /* GLAGOLITIC CAPITAL LETTER RITSI */ +$config['2c00_2c5f'][] = array('upper' => 11284, 'status' => 'C', 'lower' => array(11332)); /* GLAGOLITIC CAPITAL LETTER SLOVO */ +$config['2c00_2c5f'][] = array('upper' => 11285, 'status' => 'C', 'lower' => array(11333)); /* GLAGOLITIC CAPITAL LETTER TVRIDO */ +$config['2c00_2c5f'][] = array('upper' => 11286, 'status' => 'C', 'lower' => array(11334)); /* GLAGOLITIC CAPITAL LETTER UKU */ +$config['2c00_2c5f'][] = array('upper' => 11287, 'status' => 'C', 'lower' => array(11335)); /* GLAGOLITIC CAPITAL LETTER FRITU */ +$config['2c00_2c5f'][] = array('upper' => 11288, 'status' => 'C', 'lower' => array(11336)); /* GLAGOLITIC CAPITAL LETTER HERU */ +$config['2c00_2c5f'][] = array('upper' => 11289, 'status' => 'C', 'lower' => array(11337)); /* GLAGOLITIC CAPITAL LETTER OTU */ +$config['2c00_2c5f'][] = array('upper' => 11290, 'status' => 'C', 'lower' => array(11338)); /* GLAGOLITIC CAPITAL LETTER PE */ +$config['2c00_2c5f'][] = array('upper' => 11291, 'status' => 'C', 'lower' => array(11339)); /* GLAGOLITIC CAPITAL LETTER SHTA */ +$config['2c00_2c5f'][] = array('upper' => 11292, 'status' => 'C', 'lower' => array(11340)); /* GLAGOLITIC CAPITAL LETTER TSI */ +$config['2c00_2c5f'][] = array('upper' => 11293, 'status' => 'C', 'lower' => array(11341)); /* GLAGOLITIC CAPITAL LETTER CHRIVI */ +$config['2c00_2c5f'][] = array('upper' => 11294, 'status' => 'C', 'lower' => array(11342)); /* GLAGOLITIC CAPITAL LETTER SHA */ +$config['2c00_2c5f'][] = array('upper' => 11295, 'status' => 'C', 'lower' => array(11343)); /* GLAGOLITIC CAPITAL LETTER YERU */ +$config['2c00_2c5f'][] = array('upper' => 11296, 'status' => 'C', 'lower' => array(11344)); /* GLAGOLITIC CAPITAL LETTER YERI */ +$config['2c00_2c5f'][] = array('upper' => 11297, 'status' => 'C', 'lower' => array(11345)); /* GLAGOLITIC CAPITAL LETTER YATI */ +$config['2c00_2c5f'][] = array('upper' => 11298, 'status' => 'C', 'lower' => array(11346)); /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */ +$config['2c00_2c5f'][] = array('upper' => 11299, 'status' => 'C', 'lower' => array(11347)); /* GLAGOLITIC CAPITAL LETTER YU */ +$config['2c00_2c5f'][] = array('upper' => 11300, 'status' => 'C', 'lower' => array(11348)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS */ +$config['2c00_2c5f'][] = array('upper' => 11301, 'status' => 'C', 'lower' => array(11349)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */ +$config['2c00_2c5f'][] = array('upper' => 11302, 'status' => 'C', 'lower' => array(11350)); /* GLAGOLITIC CAPITAL LETTER YO */ +$config['2c00_2c5f'][] = array('upper' => 11303, 'status' => 'C', 'lower' => array(11351)); /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */ +$config['2c00_2c5f'][] = array('upper' => 11304, 'status' => 'C', 'lower' => array(11352)); /* GLAGOLITIC CAPITAL LETTER BIG YUS */ +$config['2c00_2c5f'][] = array('upper' => 11305, 'status' => 'C', 'lower' => array(11353)); /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */ +$config['2c00_2c5f'][] = array('upper' => 11306, 'status' => 'C', 'lower' => array(11354)); /* GLAGOLITIC CAPITAL LETTER FITA */ +$config['2c00_2c5f'][] = array('upper' => 11307, 'status' => 'C', 'lower' => array(11355)); /* GLAGOLITIC CAPITAL LETTER IZHITSA */ +$config['2c00_2c5f'][] = array('upper' => 11308, 'status' => 'C', 'lower' => array(11356)); /* GLAGOLITIC CAPITAL LETTER SHTAPIC */ +$config['2c00_2c5f'][] = array('upper' => 11309, 'status' => 'C', 'lower' => array(11357)); /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ +$config['2c00_2c5f'][] = array('upper' => 11310, 'status' => 'C', 'lower' => array(11358)); /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */ diff --git a/app/Cake/Config/unicode/casefolding/2c60_2c7f.php b/app/Cake/Config/unicode/casefolding/2c60_2c7f.php new file mode 100644 index 00000000..4c9a693e --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2c60_2c7f.php @@ -0,0 +1,48 @@ + 11360, 'status' => 'C', 'lower' => array(11361)); /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */ +$config['2c60_2c7f'][] = array('upper' => 11362, 'status' => 'C', 'lower' => array(619)); /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */ +$config['2c60_2c7f'][] = array('upper' => 11363, 'status' => 'C', 'lower' => array(7549)); /* LATIN CAPITAL LETTER P WITH STROKE */ +$config['2c60_2c7f'][] = array('upper' => 11364, 'status' => 'C', 'lower' => array(637)); /* LATIN CAPITAL LETTER R WITH TAIL */ +$config['2c60_2c7f'][] = array('upper' => 11367, 'status' => 'C', 'lower' => array(11368)); /* LATIN CAPITAL LETTER H WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11369, 'status' => 'C', 'lower' => array(11370)); /* LATIN CAPITAL LETTER K WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11371, 'status' => 'C', 'lower' => array(11372)); /* LATIN CAPITAL LETTER Z WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11381, 'status' => 'C', 'lower' => array(11382)); /* LATIN CAPITAL LETTER HALF H */ diff --git a/app/Cake/Config/unicode/casefolding/2c80_2cff.php b/app/Cake/Config/unicode/casefolding/2c80_2cff.php new file mode 100644 index 00000000..a9ce641e --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/2c80_2cff.php @@ -0,0 +1,90 @@ + 11392, 'status' => 'C', 'lower' => array(11393)); /* COPTIC CAPITAL LETTER ALFA */ +$config['2c80_2cff'][] = array('upper' => 11394, 'status' => 'C', 'lower' => array(11395)); /* COPTIC CAPITAL LETTER VIDA */ +$config['2c80_2cff'][] = array('upper' => 11396, 'status' => 'C', 'lower' => array(11397)); /* COPTIC CAPITAL LETTER GAMMA */ +$config['2c80_2cff'][] = array('upper' => 11398, 'status' => 'C', 'lower' => array(11399)); /* COPTIC CAPITAL LETTER DALDA */ +$config['2c80_2cff'][] = array('upper' => 11400, 'status' => 'C', 'lower' => array(11401)); /* COPTIC CAPITAL LETTER EIE */ +$config['2c80_2cff'][] = array('upper' => 11402, 'status' => 'C', 'lower' => array(11403)); /* COPTIC CAPITAL LETTER SOU */ +$config['2c80_2cff'][] = array('upper' => 11404, 'status' => 'C', 'lower' => array(11405)); /* COPTIC CAPITAL LETTER ZATA */ +$config['2c80_2cff'][] = array('upper' => 11406, 'status' => 'C', 'lower' => array(11407)); /* COPTIC CAPITAL LETTER HATE */ +$config['2c80_2cff'][] = array('upper' => 11408, 'status' => 'C', 'lower' => array(11409)); /* COPTIC CAPITAL LETTER THETHE */ +$config['2c80_2cff'][] = array('upper' => 11410, 'status' => 'C', 'lower' => array(11411)); /* COPTIC CAPITAL LETTER IAUDA */ +$config['2c80_2cff'][] = array('upper' => 11412, 'status' => 'C', 'lower' => array(11413)); /* COPTIC CAPITAL LETTER KAPA */ +$config['2c80_2cff'][] = array('upper' => 11414, 'status' => 'C', 'lower' => array(11415)); /* COPTIC CAPITAL LETTER LAULA */ +$config['2c80_2cff'][] = array('upper' => 11416, 'status' => 'C', 'lower' => array(11417)); /* COPTIC CAPITAL LETTER MI */ +$config['2c80_2cff'][] = array('upper' => 11418, 'status' => 'C', 'lower' => array(11419)); /* COPTIC CAPITAL LETTER NI */ +$config['2c80_2cff'][] = array('upper' => 11420, 'status' => 'C', 'lower' => array(11421)); /* COPTIC CAPITAL LETTER KSI */ +$config['2c80_2cff'][] = array('upper' => 11422, 'status' => 'C', 'lower' => array(11423)); /* COPTIC CAPITAL LETTER O */ +$config['2c80_2cff'][] = array('upper' => 11424, 'status' => 'C', 'lower' => array(11425)); /* COPTIC CAPITAL LETTER PI */ +$config['2c80_2cff'][] = array('upper' => 11426, 'status' => 'C', 'lower' => array(11427)); /* COPTIC CAPITAL LETTER RO */ +$config['2c80_2cff'][] = array('upper' => 11428, 'status' => 'C', 'lower' => array(11429)); /* COPTIC CAPITAL LETTER SIMA */ +$config['2c80_2cff'][] = array('upper' => 11430, 'status' => 'C', 'lower' => array(11431)); /* COPTIC CAPITAL LETTER TAU */ +$config['2c80_2cff'][] = array('upper' => 11432, 'status' => 'C', 'lower' => array(11433)); /* COPTIC CAPITAL LETTER UA */ +$config['2c80_2cff'][] = array('upper' => 11434, 'status' => 'C', 'lower' => array(11435)); /* COPTIC CAPITAL LETTER FI */ +$config['2c80_2cff'][] = array('upper' => 11436, 'status' => 'C', 'lower' => array(11437)); /* COPTIC CAPITAL LETTER KHI */ +$config['2c80_2cff'][] = array('upper' => 11438, 'status' => 'C', 'lower' => array(11439)); /* COPTIC CAPITAL LETTER PSI */ +$config['2c80_2cff'][] = array('upper' => 11440, 'status' => 'C', 'lower' => array(11441)); /* COPTIC CAPITAL LETTER OOU */ +$config['2c80_2cff'][] = array('upper' => 11442, 'status' => 'C', 'lower' => array(11443)); /* COPTIC CAPITAL LETTER DIALECT-P ALEF */ +$config['2c80_2cff'][] = array('upper' => 11444, 'status' => 'C', 'lower' => array(11445)); /* COPTIC CAPITAL LETTER OLD COPTIC AIN */ +$config['2c80_2cff'][] = array('upper' => 11446, 'status' => 'C', 'lower' => array(11447)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ +$config['2c80_2cff'][] = array('upper' => 11448, 'status' => 'C', 'lower' => array(11449)); /* COPTIC CAPITAL LETTER DIALECT-P KAPA */ +$config['2c80_2cff'][] = array('upper' => 11450, 'status' => 'C', 'lower' => array(11451)); /* COPTIC CAPITAL LETTER DIALECT-P NI */ +$config['2c80_2cff'][] = array('upper' => 11452, 'status' => 'C', 'lower' => array(11453)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ +$config['2c80_2cff'][] = array('upper' => 11454, 'status' => 'C', 'lower' => array(11455)); /* COPTIC CAPITAL LETTER OLD COPTIC OOU */ +$config['2c80_2cff'][] = array('upper' => 11456, 'status' => 'C', 'lower' => array(11457)); /* COPTIC CAPITAL LETTER SAMPI */ +$config['2c80_2cff'][] = array('upper' => 11458, 'status' => 'C', 'lower' => array(11459)); /* COPTIC CAPITAL LETTER CROSSED SHEI */ +$config['2c80_2cff'][] = array('upper' => 11460, 'status' => 'C', 'lower' => array(11461)); /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */ +$config['2c80_2cff'][] = array('upper' => 11462, 'status' => 'C', 'lower' => array(11463)); /* COPTIC CAPITAL LETTER OLD COPTIC ESH */ +$config['2c80_2cff'][] = array('upper' => 11464, 'status' => 'C', 'lower' => array(11465)); /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */ +$config['2c80_2cff'][] = array('upper' => 11466, 'status' => 'C', 'lower' => array(11467)); /* COPTIC CAPITAL LETTER DIALECT-P HORI */ +$config['2c80_2cff'][] = array('upper' => 11468, 'status' => 'C', 'lower' => array(11469)); /* COPTIC CAPITAL LETTER OLD COPTIC HORI */ +$config['2c80_2cff'][] = array('upper' => 11470, 'status' => 'C', 'lower' => array(11471)); /* COPTIC CAPITAL LETTER OLD COPTIC HA */ +$config['2c80_2cff'][] = array('upper' => 11472, 'status' => 'C', 'lower' => array(11473)); /* COPTIC CAPITAL LETTER L-SHAPED HA */ +$config['2c80_2cff'][] = array('upper' => 11474, 'status' => 'C', 'lower' => array(11475)); /* COPTIC CAPITAL LETTER OLD COPTIC HEI */ +$config['2c80_2cff'][] = array('upper' => 11476, 'status' => 'C', 'lower' => array(11477)); /* COPTIC CAPITAL LETTER OLD COPTIC HAT */ +$config['2c80_2cff'][] = array('upper' => 11478, 'status' => 'C', 'lower' => array(11479)); /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ +$config['2c80_2cff'][] = array('upper' => 11480, 'status' => 'C', 'lower' => array(11481)); /* COPTIC CAPITAL LETTER OLD COPTIC DJA */ +$config['2c80_2cff'][] = array('upper' => 11482, 'status' => 'C', 'lower' => array(11483)); /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ +$config['2c80_2cff'][] = array('upper' => 11484, 'status' => 'C', 'lower' => array(11485)); /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ +$config['2c80_2cff'][] = array('upper' => 11486, 'status' => 'C', 'lower' => array(11487)); /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */ +$config['2c80_2cff'][] = array('upper' => 11488, 'status' => 'C', 'lower' => array(11489)); /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */ +$config['2c80_2cff'][] = array('upper' => 11490, 'status' => 'C', 'lower' => array(11491)); /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */ diff --git a/app/Cake/Config/unicode/casefolding/ff00_ffef.php b/app/Cake/Config/unicode/casefolding/ff00_ffef.php new file mode 100644 index 00000000..43924c2c --- /dev/null +++ b/app/Cake/Config/unicode/casefolding/ff00_ffef.php @@ -0,0 +1,66 @@ + 65313, 'status' => 'C', 'lower' => array(65345)); /* FULLWIDTH LATIN CAPITAL LETTER A */ +$config['ff00_ffef'][] = array('upper' => 65314, 'status' => 'C', 'lower' => array(65346)); /* FULLWIDTH LATIN CAPITAL LETTER B */ +$config['ff00_ffef'][] = array('upper' => 65315, 'status' => 'C', 'lower' => array(65347)); /* FULLWIDTH LATIN CAPITAL LETTER C */ +$config['ff00_ffef'][] = array('upper' => 65316, 'status' => 'C', 'lower' => array(65348)); /* FULLWIDTH LATIN CAPITAL LETTER D */ +$config['ff00_ffef'][] = array('upper' => 65317, 'status' => 'C', 'lower' => array(65349)); /* FULLWIDTH LATIN CAPITAL LETTER E */ +$config['ff00_ffef'][] = array('upper' => 65318, 'status' => 'C', 'lower' => array(65350)); /* FULLWIDTH LATIN CAPITAL LETTER F */ +$config['ff00_ffef'][] = array('upper' => 65319, 'status' => 'C', 'lower' => array(65351)); /* FULLWIDTH LATIN CAPITAL LETTER G */ +$config['ff00_ffef'][] = array('upper' => 65320, 'status' => 'C', 'lower' => array(65352)); /* FULLWIDTH LATIN CAPITAL LETTER H */ +$config['ff00_ffef'][] = array('upper' => 65321, 'status' => 'C', 'lower' => array(65353)); /* FULLWIDTH LATIN CAPITAL LETTER I */ +$config['ff00_ffef'][] = array('upper' => 65322, 'status' => 'C', 'lower' => array(65354)); /* FULLWIDTH LATIN CAPITAL LETTER J */ +$config['ff00_ffef'][] = array('upper' => 65323, 'status' => 'C', 'lower' => array(65355)); /* FULLWIDTH LATIN CAPITAL LETTER K */ +$config['ff00_ffef'][] = array('upper' => 65324, 'status' => 'C', 'lower' => array(65356)); /* FULLWIDTH LATIN CAPITAL LETTER L */ +$config['ff00_ffef'][] = array('upper' => 65325, 'status' => 'C', 'lower' => array(65357)); /* FULLWIDTH LATIN CAPITAL LETTER M */ +$config['ff00_ffef'][] = array('upper' => 65326, 'status' => 'C', 'lower' => array(65358)); /* FULLWIDTH LATIN CAPITAL LETTER N */ +$config['ff00_ffef'][] = array('upper' => 65327, 'status' => 'C', 'lower' => array(65359)); /* FULLWIDTH LATIN CAPITAL LETTER O */ +$config['ff00_ffef'][] = array('upper' => 65328, 'status' => 'C', 'lower' => array(65360)); /* FULLWIDTH LATIN CAPITAL LETTER P */ +$config['ff00_ffef'][] = array('upper' => 65329, 'status' => 'C', 'lower' => array(65361)); /* FULLWIDTH LATIN CAPITAL LETTER Q */ +$config['ff00_ffef'][] = array('upper' => 65330, 'status' => 'C', 'lower' => array(65362)); /* FULLWIDTH LATIN CAPITAL LETTER R */ +$config['ff00_ffef'][] = array('upper' => 65331, 'status' => 'C', 'lower' => array(65363)); /* FULLWIDTH LATIN CAPITAL LETTER S */ +$config['ff00_ffef'][] = array('upper' => 65332, 'status' => 'C', 'lower' => array(65364)); /* FULLWIDTH LATIN CAPITAL LETTER T */ +$config['ff00_ffef'][] = array('upper' => 65333, 'status' => 'C', 'lower' => array(65365)); /* FULLWIDTH LATIN CAPITAL LETTER U */ +$config['ff00_ffef'][] = array('upper' => 65334, 'status' => 'C', 'lower' => array(65366)); /* FULLWIDTH LATIN CAPITAL LETTER V */ +$config['ff00_ffef'][] = array('upper' => 65335, 'status' => 'C', 'lower' => array(65367)); /* FULLWIDTH LATIN CAPITAL LETTER W */ +$config['ff00_ffef'][] = array('upper' => 65336, 'status' => 'C', 'lower' => array(65368)); /* FULLWIDTH LATIN CAPITAL LETTER X */ +$config['ff00_ffef'][] = array('upper' => 65337, 'status' => 'C', 'lower' => array(65369)); /* FULLWIDTH LATIN CAPITAL LETTER Y */ +$config['ff00_ffef'][] = array('upper' => 65338, 'status' => 'C', 'lower' => array(65370)); /* FULLWIDTH LATIN CAPITAL LETTER Z */ diff --git a/app/Cake/Configure/IniReader.php b/app/Cake/Configure/IniReader.php new file mode 100644 index 00000000..4efb0374 --- /dev/null +++ b/app/Cake/Configure/IniReader.php @@ -0,0 +1,137 @@ + array('password' => 'secret'))` + * + * You can nest properties as deeply as needed using `.`'s. In addition to using `.` you + * can use standard ini section notation to create nested structures: + * + * {{{ + * [section] + * key = value + * }}} + * + * Once loaded into Configure, the above would be accessed using: + * + * `Configure::read('section.key'); + * + * You can combine `.` separated values with sections to create more deeply + * nested structures. + * + * IniReader also manipulates how the special ini values of + * 'yes', 'no', 'on', 'off', 'null' are handled. These values will be + * converted to their boolean equivalents. + * + * @package Cake.Configure + * @see http://php.net/parse_ini_file + */ +class IniReader implements ConfigReaderInterface { + +/** + * The path to read ini files from. + * + * @var array + */ + protected $_path; + +/** + * The section to read, if null all sections will be read. + * + * @var string + */ + protected $_section; + +/** + * Build and construct a new ini file parser. The parser can be used to read + * ini files that are on the filesystem. + * + * @param string $path Path to load ini config files from. + * @param string $section Only get one section, leave null to parse and fetch + * all sections in the ini file. + */ + public function __construct($path, $section = null) { + $this->_path = $path; + $this->_section = $section; + } + +/** + * Read an ini file and return the results as an array. + * + * @param string $file Name of the file to read. The chosen file + * must be on the reader's path. + * @return array + * @throws ConfigureException + */ + public function read($file) { + $filename = $this->_path . $file; + if (!file_exists($filename)) { + $filename .= '.ini'; + if (!file_exists($filename)) { + throw new ConfigureException(__d('cake_dev', 'Could not load configuration files: %s or %s', substr($filename, 0, -4), $filename)); + } + } + $contents = parse_ini_file($filename, true); + if (!empty($this->_section) && isset($contents[$this->_section])) { + $values = $this->_parseNestedValues($contents[$this->_section]); + } else { + $values = array(); + foreach ($contents as $section => $attribs) { + if (is_array($attribs)) { + $values[$section] = $this->_parseNestedValues($attribs); + } else { + $parse = $this->_parseNestedValues(array($attribs)); + $values[$section] = array_shift($parse); + } + } + } + return $values; + } + +/** + * parses nested values out of keys. + * + * @param array $values Values to be exploded. + * @return array Array of values exploded + */ + protected function _parseNestedValues($values) { + foreach ($values as $key => $value) { + if ($value === '1') { + $value = true; + } + if ($value === '') { + $value = false; + } + if (strpos($key, '.') !== false) { + $values = Set::insert($values, $key, $value); + } else { + $values[$key] = $value; + } + } + return $values; + } +} diff --git a/app/Cake/Configure/PhpReader.php b/app/Cake/Configure/PhpReader.php new file mode 100644 index 00000000..a19c0771 --- /dev/null +++ b/app/Cake/Configure/PhpReader.php @@ -0,0 +1,89 @@ + + * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package Cake.Configure + * @since CakePHP(tm) v 2.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +/** + * PHP Reader allows Configure to load configuration values from + * files containing simple PHP arrays. + * + * Files compatible with PhpReader should define a `$config` variable, that + * contains all of the configuration data contained in the file. + * + * @package Cake.Configure + */ +class PhpReader implements ConfigReaderInterface { +/** + * The path this reader finds files on. + * + * @var string + */ + protected $_path = null; + +/** + * Constructor for PHP Config file reading. + * + * @param string $path The path to read config files from. Defaults to APP . 'Config' . DS + */ + public function __construct($path = null) { + if (!$path) { + $path = APP . 'Config' . DS; + } + $this->_path = $path; + } + +/** + * Read a config file and return its contents. + * + * Files with `.` in the name will be treated as values in plugins. Instead of reading from + * the initialized path, plugin keys will be located using App::pluginPath(). + * + * @param string $key The identifier to read from. If the key has a . it will be treated + * as a plugin prefix. + * @return array Parsed configuration values. + * @throws ConfigureException when files don't exist or they don't contain `$config`. + * Or when files contain '..' as this could lead to abusive reads. + */ + public function read($key) { + if (strpos($key, '..') !== false) { + throw new ConfigureException(__d('cake_dev', 'Cannot load configuration files with ../ in them.')); + } + if (substr($key, -4) === '.php') { + $key = substr($key, 0, -4); + } + list($plugin, $key) = pluginSplit($key); + + if ($plugin) { + $file = App::pluginPath($plugin) . 'Config' . DS . $key; + } else { + $file = $this->_path . $key; + } + if (!file_exists($file)) { + $file .= '.php'; + if (!file_exists($file)) { + throw new ConfigureException(__d('cake_dev', 'Could not load configuration files: %s or %s', substr($file, 0, -4), $file)); + } + } + include $file; + if (!isset($config)) { + throw new ConfigureException( + sprintf(__d('cake_dev', 'No variable $config found in %s.php'), $file) + ); + } + return $config; + } +} diff --git a/app/Cake/Controller/AppController.php b/app/Cake/Controller/AppController.php new file mode 100644 index 00000000..d9712dc0 --- /dev/null +++ b/app/Cake/Controller/AppController.php @@ -0,0 +1,36 @@ +constructClasses(); + $this->Components->trigger('initialize', array(&$this)); + $this->_set(array('cacheAction' => false, 'viewPath' => 'Errors')); + } + +/** + * Escapes the viewVars. + * + * @return void + */ + public function beforeRender() { + parent::beforeRender(); + foreach ($this->viewVars as $key => $value) { + if (!is_object($value)){ + $this->viewVars[$key] = h($value); + } + } + } +} diff --git a/app/Cake/Controller/Component.php b/app/Cake/Controller/Component.php new file mode 100644 index 00000000..3bd0232b --- /dev/null +++ b/app/Cake/Controller/Component.php @@ -0,0 +1,157 @@ +_Collection = $collection; + $this->settings = $settings; + $this->_set($settings); + if (!empty($this->components)) { + $this->_componentMap = ComponentCollection::normalizeObjectArray($this->components); + } + } + +/** + * Magic method for lazy loading $components. + * + * @param string $name Name of component to get. + * @return mixed A Component object or null. + */ + public function __get($name) { + if (isset($this->_componentMap[$name]) && !isset($this->{$name})) { + $settings = array_merge((array)$this->_componentMap[$name]['settings'], array('enabled' => false)); + $this->{$name} = $this->_Collection->load($this->_componentMap[$name]['class'], $settings); + } + if (isset($this->{$name})) { + return $this->{$name}; + } + } + +/** + * Called before the Controller::beforeFilter(). + * + * @param Controller $controller Controller with components to initialize + * @return void + * @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components + */ + public function initialize($controller) { } + +/** + * Called after the Controller::beforeFilter() and before the controller action + * + * @param Controller $controller Controller with components to startup + * @return void + * @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components + */ + public function startup($controller) { } + +/** + * Called after the Controller::beforeRender(), after the view class is loaded, and before the + * Controller::render() + * + * @param Controller $controller Controller with components to beforeRender + * @return void + */ + public function beforeRender($controller) { } + +/** + * Called after Controller::render() and before the output is printed to the browser. + * + * @param Controller $controller Controller with components to shutdown + * @return void + */ + public function shutdown($controller) { } + +/** + * Called before Controller::redirect(). Allows you to replace the url that will + * be redirected to with a new url. The return of this method can either be an array or a string. + * + * If the return is an array and contains a 'url' key. You may also supply the following: + * + * - `status` The status code for the redirect + * - `exit` Whether or not the redirect should exit. + * + * If your response is a string or an array that does not contain a 'url' key it will + * be used as the new url to redirect to. + * + * @param Controller $controller Controller with components to beforeRedirect + * @param string|array $url Either the string or url array that is being redirected to. + * @param integer $status The status code of the redirect + * @param boolean $exit Will the script exit. + * @return array|null Either an array or null. + */ + public function beforeRedirect($controller, $url, $status = null, $exit = true) {} + +} diff --git a/app/Cake/Controller/Component/AclComponent.php b/app/Cake/Controller/Component/AclComponent.php new file mode 100644 index 00000000..c34fb7a5 --- /dev/null +++ b/app/Cake/Controller/Component/AclComponent.php @@ -0,0 +1,675 @@ +adapter($name); + } + +/** + * Sets or gets the Adapter object currently in the AclComponent. + * + * `$this->Acl->adapter();` will get the current adapter class while + * `$this->Acl->adapter($obj);` will set the adapter class + * + * Will call the initialize method on the adapter if setting a new one. + * + * @param mixed $adapter Instance of AclBase or a string name of the class to use. (optional) + * @return mixed either null, or instance of AclBase + * @throws CakeException when the given class is not an AclBase + */ + public function adapter($adapter = null) { + if ($adapter) { + if (is_string($adapter)) { + $adapter = new $adapter(); + } + if (!$adapter instanceof AclInterface) { + throw new CakeException(__d('cake_dev', 'AclComponent adapters must implement AclInterface')); + } + $this->_Instance = $adapter; + $this->_Instance->initialize($this); + return; + } + return $this->_Instance; + } + +/** + * Pass-thru function for ACL check instance. Check methods + * are used to check whether or not an ARO can access an ACO + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function check($aro, $aco, $action = "*") { + return $this->_Instance->check($aro, $aco, $action); + } + +/** + * Pass-thru function for ACL allow instance. Allow methods + * are used to grant an ARO access to an ACO. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function allow($aro, $aco, $action = "*") { + return $this->_Instance->allow($aro, $aco, $action); + } + +/** + * Pass-thru function for ACL deny instance. Deny methods + * are used to remove permission from an ARO to access an ACO. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function deny($aro, $aco, $action = "*") { + return $this->_Instance->deny($aro, $aco, $action); + } + +/** + * Pass-thru function for ACL inherit instance. Inherit methods + * modify the permission for an ARO to be that of its parent object. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function inherit($aro, $aco, $action = "*") { + return $this->_Instance->inherit($aro, $aco, $action); + } + +/** + * Pass-thru function for ACL grant instance. An alias for AclComponent::allow() + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + * @deprecated + */ + public function grant($aro, $aco, $action = "*") { + trigger_error(__d('cake_dev', 'AclComponent::grant() is deprecated, use allow() instead'), E_USER_WARNING); + return $this->_Instance->allow($aro, $aco, $action); + } + +/** + * Pass-thru function for ACL grant instance. An alias for AclComponent::deny() + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + * @deprecated + */ + public function revoke($aro, $aco, $action = "*") { + trigger_error(__d('cake_dev', 'AclComponent::revoke() is deprecated, use deny() instead'), E_USER_WARNING); + return $this->_Instance->deny($aro, $aco, $action); + } +} + +/** + * Access Control List interface. + * Implementing classes are used by AclComponent to perform ACL checks in Cake. + * + * @package Cake.Controller.Component + */ +interface AclInterface { + +/** + * Empty method to be overridden in subclasses + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + */ + public function check($aro, $aco, $action = "*"); + +/** + * Allow methods are used to grant an ARO access to an ACO. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function allow($aro, $aco, $action = "*"); + +/** + * Deny methods are used to remove permission from an ARO to access an ACO. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function deny($aro, $aco, $action = "*"); + +/** + * Inherit methods modify the permission for an ARO to be that of its parent object. + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function inherit($aro, $aco, $action = "*"); + +/** + * Initialization method for the Acl implementation + * + * @param AclComponent $component + */ + public function initialize($component); +} + +/** + * DbAcl implements an ACL control system in the database. ARO's and ACO's are + * structured into trees and a linking table is used to define permissions. You + * can install the schema for DbAcl with the Schema Shell. + * + * `$aco` and `$aro` parameters can be slash delimited paths to tree nodes. + * + * eg. `controllers/Users/edit` + * + * Would point to a tree structure like + * + * {{{ + * controllers + * Users + * edit + * }}} + * + * @package Cake.Controller.Component + */ +class DbAcl extends Object implements AclInterface { + +/** + * Constructor + * + */ + public function __construct() { + parent::__construct(); + App::uses('AclNode', 'Model'); + $this->Aro = ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro')); + $this->Aco = ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco')); + } + +/** + * Initializes the containing component and sets the Aro/Aco objects to it. + * + * @param AclComponent $component + * @return void + */ + public function initialize($component) { + $component->Aro = $this->Aro; + $component->Aco = $this->Aco; + } + +/** + * Checks if the given $aro has access to action $action in $aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success (true if ARO has access to action in ACO, false otherwise) + * @link http://book.cakephp.org/view/1249/Checking-Permissions-The-ACL-Component + */ + public function check($aro, $aco, $action = "*") { + if ($aro == null || $aco == null) { + return false; + } + + $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema()); + $aroPath = $this->Aro->node($aro); + $acoPath = $this->Aco->node($aco); + + if (empty($aroPath) || empty($acoPath)) { + trigger_error(__d('cake_dev', "DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING); + return false; + } + + if ($acoPath == null || $acoPath == array()) { + trigger_error(__d('cake_dev', "DbAcl::check() - Failed ACO node lookup in permissions check. Node references:\nAro: ") . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING); + return false; + } + + if ($action != '*' && !in_array('_' . $action, $permKeys)) { + trigger_error(__d('cake_dev', "ACO permissions key %s does not exist in DbAcl::check()", $action), E_USER_NOTICE); + return false; + } + + $inherited = array(); + $acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id'); + + $count = count($aroPath); + for ($i = 0 ; $i < $count; $i++) { + $permAlias = $this->Aro->Permission->alias; + + $perms = $this->Aro->Permission->find('all', array( + 'conditions' => array( + "{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'], + "{$permAlias}.aco_id" => $acoIDs + ), + 'order' => array($this->Aco->alias . '.lft' => 'desc'), + 'recursive' => 0 + )); + + if (empty($perms)) { + continue; + } else { + $perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias); + foreach ($perms as $perm) { + if ($action == '*') { + + foreach ($permKeys as $key) { + if (!empty($perm)) { + if ($perm[$key] == -1) { + return false; + } elseif ($perm[$key] == 1) { + $inherited[$key] = 1; + } + } + } + + if (count($inherited) === count($permKeys)) { + return true; + } + } else { + switch ($perm['_' . $action]) { + case -1: + return false; + case 0: + continue; + break; + case 1: + return true; + break; + } + } + } + } + } + return false; + } + +/** + * Allow $aro to have access to action $actions in $aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $actions Action (defaults to *) + * @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit) + * @return boolean Success + * @link http://book.cakephp.org/view/1248/Assigning-Permissions + */ + public function allow($aro, $aco, $actions = "*", $value = 1) { + $perms = $this->getAclLink($aro, $aco); + $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema()); + $save = array(); + + if ($perms == false) { + trigger_error(__d('cake_dev', 'DbAcl::allow() - Invalid node'), E_USER_WARNING); + return false; + } + if (isset($perms[0])) { + $save = $perms[0][$this->Aro->Permission->alias]; + } + + if ($actions == "*") { + $permKeys = $this->_getAcoKeys($this->Aro->Permission->schema()); + $save = array_combine($permKeys, array_pad(array(), count($permKeys), $value)); + } else { + if (!is_array($actions)) { + $actions = array('_' . $actions); + } + if (is_array($actions)) { + foreach ($actions as $action) { + if ($action{0} != '_') { + $action = '_' . $action; + } + if (in_array($action, $permKeys)) { + $save[$action] = $value; + } + } + } + } + list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']); + + if ($perms['link'] != null && !empty($perms['link'])) { + $save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id']; + } else { + unset($save['id']); + $this->Aro->Permission->id = null; + } + return ($this->Aro->Permission->save($save) !== false); + } + +/** + * Deny access for $aro to action $action in $aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + * @link http://book.cakephp.org/view/1248/Assigning-Permissions + */ + public function deny($aro, $aco, $action = "*") { + return $this->allow($aro, $aco, $action, -1); + } + +/** + * Let access for $aro to action $action in $aco be inherited + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function inherit($aro, $aco, $action = "*") { + return $this->allow($aro, $aco, $action, 0); + } + +/** + * Allow $aro to have access to action $actions in $aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + * @see allow() + */ + public function grant($aro, $aco, $action = "*") { + return $this->allow($aro, $aco, $action); + } + +/** + * Deny access for $aro to action $action in $aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + * @see deny() + */ + public function revoke($aro, $aco, $action = "*") { + return $this->deny($aro, $aco, $action); + } + +/** + * Get an array of access-control links between the given Aro and Aco + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @return array Indexed array with: 'aro', 'aco' and 'link' + */ + public function getAclLink($aro, $aco) { + $obj = array(); + $obj['Aro'] = $this->Aro->node($aro); + $obj['Aco'] = $this->Aco->node($aco); + + if (empty($obj['Aro']) || empty($obj['Aco'])) { + return false; + } + + return array( + 'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'), + 'aco' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'), + 'link' => $this->Aro->Permission->find('all', array('conditions' => array( + $this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'), + $this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id') + ))) + ); + } + +/** + * Get the keys used in an ACO + * + * @param array $keys Permission model info + * @return array ACO keys + */ + protected function _getAcoKeys($keys) { + $newKeys = array(); + $keys = array_keys($keys); + foreach ($keys as $key) { + if (!in_array($key, array('id', 'aro_id', 'aco_id'))) { + $newKeys[] = $key; + } + } + return $newKeys; + } +} + +/** + * IniAcl implements an access control system using an INI file. An example + * of the ini file used can be found in /config/acl.ini.php. + * + * @package Cake.Controller.Component + */ +class IniAcl extends Object implements AclInterface { + +/** + * Array with configuration, parsed from ini file + * + * @var array + */ + public $config = null; + +/** + * The Set::classicExtract() path to the user/aro identifier in the + * acl.ini file. This path will be used to extract the string + * representation of a user used in the ini file. + * + * @var string + */ + public $userPath = 'User.username'; + +/** + * Initialize method + * + * @param AclBase $component + * @return void + */ + public function initialize($component) { + + } + +/** + * No op method, allow cannot be done with IniAcl + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function allow($aro, $aco, $action = "*") { + + } + +/** + * No op method, deny cannot be done with IniAcl + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function deny($aro, $aco, $action = "*") { + + } + +/** + * No op method, inherit cannot be done with IniAcl + * + * @param string $aro ARO The requesting object identifier. + * @param string $aco ACO The controlled object identifier. + * @param string $action Action (defaults to *) + * @return boolean Success + */ + public function inherit($aro, $aco, $action = "*") { + + } + +/** + * Main ACL check function. Checks to see if the ARO (access request object) has access to the + * ACO (access control object).Looks at the acl.ini.php file for permissions + * (see instructions in /config/acl.ini.php). + * + * @param string $aro ARO + * @param string $aco ACO + * @param string $aco_action Action + * @return boolean Success + */ + public function check($aro, $aco, $aco_action = null) { + if ($this->config == null) { + $this->config = $this->readConfigFile(APP . 'Config' . DS . 'acl.ini.php'); + } + $aclConfig = $this->config; + + if (is_array($aro)) { + $aro = Set::classicExtract($aro, $this->userPath); + } + + if (isset($aclConfig[$aro]['deny'])) { + $userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny'])); + + if (array_search($aco, $userDenies)) { + return false; + } + } + + if (isset($aclConfig[$aro]['allow'])) { + $userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow'])); + + if (array_search($aco, $userAllows)) { + return true; + } + } + + if (isset($aclConfig[$aro]['groups'])) { + $userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups'])); + + foreach ($userGroups as $group) { + if (array_key_exists($group, $aclConfig)) { + if (isset($aclConfig[$group]['deny'])) { + $groupDenies = $this->arrayTrim(explode(",", $aclConfig[$group]['deny'])); + + if (array_search($aco, $groupDenies)) { + return false; + } + } + + if (isset($aclConfig[$group]['allow'])) { + $groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow'])); + + if (array_search($aco, $groupAllows)) { + return true; + } + } + } + } + } + return false; + } + +/** + * Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly. + * + * @param string $filename File + * @return array INI section structure + */ + public function readConfigFile($filename) { + App::uses('IniReader', 'Configure'); + $iniFile = new IniReader(dirname($filename) . DS); + return $iniFile->read(basename($filename)); + } + +/** + * Removes trailing spaces on all array elements (to prepare for searching) + * + * @param array $array Array to trim + * @return array Trimmed array + */ + public function arrayTrim($array) { + foreach ($array as $key => $value) { + $array[$key] = trim($value); + } + array_unshift($array, ""); + return $array; + } +} diff --git a/app/Cake/Controller/Component/Auth/ActionsAuthorize.php b/app/Cake/Controller/Component/Auth/ActionsAuthorize.php new file mode 100644 index 00000000..88c915fe --- /dev/null +++ b/app/Cake/Controller/Component/Auth/ActionsAuthorize.php @@ -0,0 +1,41 @@ +_Collection->load('Acl'); + $user = array($this->settings['userModel'] => $user); + return $Acl->check($user, $this->action($request)); + } +} diff --git a/app/Cake/Controller/Component/Auth/BaseAuthenticate.php b/app/Cake/Controller/Component/Auth/BaseAuthenticate.php new file mode 100644 index 00000000..7fa44e7c --- /dev/null +++ b/app/Cake/Controller/Component/Auth/BaseAuthenticate.php @@ -0,0 +1,123 @@ + 1).` + * + * @var array + */ + public $settings = array( + 'fields' => array( + 'username' => 'username', + 'password' => 'password' + ), + 'userModel' => 'User', + 'scope' => array() + ); + +/** + * A Component collection, used to get more components. + * + * @var ComponentCollection + */ + protected $_Collection; + +/** + * Constructor + * + * @param ComponentCollection $collection The Component collection used on this request. + * @param array $settings Array of settings to use. + */ + public function __construct(ComponentCollection $collection, $settings) { + $this->_Collection = $collection; + $this->settings = Set::merge($this->settings, $settings); + } + +/** + * Find a user record using the standard options. + * + * @param string $username The username/identifier. + * @param string $password The unhashed password. + * @return Mixed Either false on failure, or an array of user data. + */ + protected function _findUser($username, $password) { + $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + $fields = $this->settings['fields']; + + $conditions = array( + $model . '.' . $fields['username'] => $username, + $model . '.' . $fields['password'] => AuthComponent::password($password), + ); + if (!empty($this->settings['scope'])) { + $conditions = array_merge($conditions, $this->settings['scope']); + } + $result = ClassRegistry::init($userModel)->find('first', array( + 'conditions' => $conditions, + 'recursive' => 0 + )); + if (empty($result) || empty($result[$model])) { + return false; + } + unset($result[$model][$fields['password']]); + return $result[$model]; + } + +/** + * Authenticate a user based on the request information. + * + * @param CakeRequest $request Request to get authentication information from. + * @param CakeResponse $response A response object that can have headers added. + * @return mixed Either false on failure, or an array of user data on success. + */ + abstract public function authenticate(CakeRequest $request, CakeResponse $response); + +/** + * Allows you to hook into AuthComponent::logout(), + * and implement specialized logout behaviour. + * + * All attached authentication objects will have this method + * called when a user logs out. + * + * @param array $user The user about to be logged out. + * @return void + */ + public function logout($user) { } + +/** + * Get a user based on information in the request. Primarily used by stateless authentication + * systems like basic and digest auth. + * + * @param CakeRequest $request Request object. + * @return mixed Either false or an array of user information + */ + public function getUser($request) { + return false; + } +} diff --git a/app/Cake/Controller/Component/Auth/BaseAuthorize.php b/app/Cake/Controller/Component/Auth/BaseAuthorize.php new file mode 100644 index 00000000..d68f7b1b --- /dev/null +++ b/app/Cake/Controller/Component/Auth/BaseAuthorize.php @@ -0,0 +1,139 @@ +action(); + * - `actionMap` - Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles. + * - `userModel` - Model name that ARO records can be found under. Defaults to 'User'. + * + * @var array + */ + public $settings = array( + 'actionPath' => null, + 'actionMap' => array( + 'index' => 'read', + 'add' => 'create', + 'edit' => 'update', + 'view' => 'read', + 'delete' => 'delete', + 'remove' => 'delete' + ), + 'userModel' => 'User' + ); + +/** + * Constructor + * + * @param ComponentCollection $collection The controller for this request. + * @param string $settings An array of settings. This class does not use any settings. + */ + public function __construct(ComponentCollection $collection, $settings = array()) { + $this->_Collection = $collection; + $controller = $collection->getController(); + $this->controller($controller); + $this->settings = Set::merge($this->settings, $settings); + } + +/** + * Checks user authorization. + * + * @param array $user Active user data + * @param CakeRequest $request + * @return boolean + */ + abstract public function authorize($user, CakeRequest $request); + +/** + * Accessor to the controller object. + * + * @param mixed $controller null to get, a controller to set. + * @return mixed + * @throws CakeException + */ + public function controller($controller = null) { + if ($controller) { + if (!$controller instanceof Controller) { + throw new CakeException(__d('cake_dev', '$controller needs to be an instance of Controller')); + } + $this->_Controller = $controller; + return true; + } + return $this->_Controller; + } + +/** + * Get the action path for a given request. Primarily used by authorize objects + * that need to get information about the plugin, controller, and action being invoked. + * + * @param CakeRequest $request The request a path is needed for. + * @param string $path + * @return string the action path for the given request. + */ + public function action($request, $path = '/:plugin/:controller/:action') { + $plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']) . '/'; + return str_replace( + array(':controller', ':action', ':plugin/'), + array(Inflector::camelize($request['controller']), $request['action'], $plugin), + $this->settings['actionPath'] . $path + ); + } + +/** + * Maps crud actions to actual controller names. Used to modify or get the current mapped actions. + * + * @param mixed $map Either an array of mappings, or undefined to get current values. + * @return mixed Either the current mappings or null when setting. + */ + public function mapActions($map = array()) { + if (empty($map)) { + return $this->settings['actionMap']; + } + $crud = array('create', 'read', 'update', 'delete'); + foreach ($map as $action => $type) { + if (in_array($action, $crud) && is_array($type)) { + foreach ($type as $typedAction) { + $this->settings['actionMap'][$typedAction] = $action; + } + } else { + $this->settings['actionMap'][$action] = $type; + } + } + } +} diff --git a/app/Cake/Controller/Component/Auth/BasicAuthenticate.php b/app/Cake/Controller/Component/Auth/BasicAuthenticate.php new file mode 100644 index 00000000..347deec9 --- /dev/null +++ b/app/Cake/Controller/Component/Auth/BasicAuthenticate.php @@ -0,0 +1,122 @@ + array( + * 'authenticate' => array('Basic') + * ) + * ); + * }}} + * + * In your login function just call `$this->Auth->login()` without any checks for POST data. This + * will send the authentication headers, and trigger the login dialog in the browser/client. + * + * @package Cake.Controller.Component.Auth + * @since 2.0 + */ +class BasicAuthenticate extends BaseAuthenticate { +/** + * Settings for this object. + * + * - `fields` The fields to use to identify a user by. + * - `userModel` The model name of the User, defaults to User. + * - `scope` Additional conditions to use when looking up and authenticating users, + * i.e. `array('User.is_active' => 1).` + * - `realm` The realm authentication is for. Defaults the server name. + * + * @var array + */ + public $settings = array( + 'fields' => array( + 'username' => 'username', + 'password' => 'password' + ), + 'userModel' => 'User', + 'scope' => array(), + 'realm' => '', + ); + +/** + * Constructor, completes configuration for basic authentication. + * + * @param ComponentCollection $collection The Component collection used on this request. + * @param array $settings An array of settings. + */ + public function __construct(ComponentCollection $collection, $settings) { + parent::__construct($collection, $settings); + if (empty($this->settings['realm'])) { + $this->settings['realm'] = env('SERVER_NAME'); + } + } + +/** + * Authenticate a user using basic HTTP auth. Will use the configured User model and attempt a + * login using basic HTTP auth. + * + * @param CakeRequest $request The request to authenticate with. + * @param CakeResponse $response The response to add headers to. + * @return mixed Either false on failure, or an array of user data on success. + */ + public function authenticate(CakeRequest $request, CakeResponse $response) { + $result = $this->getUser($request); + + if (empty($result)) { + $response->header($this->loginHeaders()); + $response->statusCode(401); + $response->send(); + return false; + } + return $result; + } + +/** + * Get a user based on information in the request. Used by cookie-less auth for stateless clients. + * + * @param CakeRequest $request Request object. + * @return mixed Either false or an array of user information + */ + public function getUser($request) { + $username = env('PHP_AUTH_USER'); + $pass = env('PHP_AUTH_PW'); + + if (empty($username) || empty($pass)) { + return false; + } + return $this->_findUser($username, $pass); + } + +/** + * Generate the login headers + * + * @return string Headers for logging in. + */ + public function loginHeaders() { + return sprintf('WWW-Authenticate: Basic realm="%s"', $this->settings['realm']); + } +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/Auth/ControllerAuthorize.php b/app/Cake/Controller/Component/Auth/ControllerAuthorize.php new file mode 100644 index 00000000..9328cace --- /dev/null +++ b/app/Cake/Controller/Component/Auth/ControllerAuthorize.php @@ -0,0 +1,67 @@ +request->params['admin'])) { + * return $user['role'] == 'admin'; + * } + * return !empty($user); + * } + * }}} + * + * the above is simple implementation that would only authorize users of the 'admin' role to access + * admin routing. + * + * @package Cake.Controller.Component.Auth + * @since 2.0 + * @see AuthComponent::$authenticate + */ +class ControllerAuthorize extends BaseAuthorize { + +/** + * Get/set the controller this authorize object will be working with. Also checks that isAuthorized is implemented. + * + * @param mixed $controller null to get, a controller to set. + * @return mixed + * @throws CakeException + */ + public function controller($controller = null) { + if ($controller) { + if (!method_exists($controller, 'isAuthorized')) { + throw new CakeException(__d('cake_dev', '$controller does not implement an isAuthorized() method.')); + } + } + return parent::controller($controller); + } + +/** + * Checks user authorization using a controller callback. + * + * @param array $user Active user data + * @param CakeRequest $request + * @return boolean + */ + public function authorize($user, CakeRequest $request) { + return (bool) $this->_Controller->isAuthorized($user); + } + +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/Auth/CrudAuthorize.php b/app/Cake/Controller/Component/Auth/CrudAuthorize.php new file mode 100644 index 00000000..947cb9b6 --- /dev/null +++ b/app/Cake/Controller/Component/Auth/CrudAuthorize.php @@ -0,0 +1,100 @@ +_setPrefixMappings(); + } + +/** + * sets the crud mappings for prefix routes. + * + * @return void + */ + protected function _setPrefixMappings() { + $crud = array('create', 'read', 'update', 'delete'); + $map = array_combine($crud, $crud); + + $prefixes = Router::prefixes(); + if (!empty($prefixes)) { + foreach ($prefixes as $prefix) { + $map = array_merge($map, array( + $prefix . '_index' => 'read', + $prefix . '_add' => 'create', + $prefix . '_edit' => 'update', + $prefix . '_view' => 'read', + $prefix . '_remove' => 'delete', + $prefix . '_create' => 'create', + $prefix . '_read' => 'read', + $prefix . '_update' => 'update', + $prefix . '_delete' => 'delete' + )); + } + } + $this->mapActions($map); + } + +/** + * Authorize a user using the mapped actions and the AclComponent. + * + * @param array $user The user to authorize + * @param CakeRequest $request The request needing authorization. + * @return boolean + */ + public function authorize($user, CakeRequest $request) { + if (!isset($this->settings['actionMap'][$request->params['action']])) { + trigger_error(__d('cake_dev', + 'CrudAuthorize::authorize() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', + $request->action, + $request->controller + ), + E_USER_WARNING + ); + return false; + } + $Acl = $this->_Collection->load('Acl'); + return $Acl->check( + $user, + $this->action($request, ':controller'), + $this->settings['actionMap'][$request->params['action']] + ); + } +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/Auth/DigestAuthenticate.php b/app/Cake/Controller/Component/Auth/DigestAuthenticate.php new file mode 100644 index 00000000..0e3d004b --- /dev/null +++ b/app/Cake/Controller/Component/Auth/DigestAuthenticate.php @@ -0,0 +1,262 @@ + array( + * 'authenticate' => array('Digest') + * ) + * ); + * }}} + * + * In your login function just call `$this->Auth->login()` without any checks for POST data. This + * will send the authentication headers, and trigger the login dialog in the browser/client. + * + * ### Generating passwords compatible with Digest authentication. + * + * Due to the Digest authentication specification, digest auth requires a special password value. You + * can generate this password using `DigestAuthenticate::password()` + * + * `$digestPass = DigestAuthenticate::password($username, env('SERVER_NAME'), $password);` + * + * Its recommended that you store this digest auth only password separate from password hashes used for other + * login methods. For example `User.digest_pass` could be used for a digest password, while `User.password` would + * store the password hash for use with other methods like Basic or Form. + * + * @package Cake.Controller.Component.Auth + * @since 2.0 + */ +class DigestAuthenticate extends BaseAuthenticate { +/** + * Settings for this object. + * + * - `fields` The fields to use to identify a user by. + * - `userModel` The model name of the User, defaults to User. + * - `scope` Additional conditions to use when looking up and authenticating users, + * i.e. `array('User.is_active' => 1).` + * - `realm` The realm authentication is for, Defaults to the servername. + * - `nonce` A nonce used for authentication. Defaults to `uniqid()`. + * - `qop` Defaults to auth, no other values are supported at this time. + * - `opaque` A string that must be returned unchanged by clients. Defaults to `md5($settings['realm'])` + * + * @var array + */ + public $settings = array( + 'fields' => array( + 'username' => 'username', + 'password' => 'password' + ), + 'userModel' => 'User', + 'scope' => array(), + 'realm' => '', + 'qop' => 'auth', + 'nonce' => '', + 'opaque' => '' + ); + +/** + * Constructor, completes configuration for digest authentication. + * + * @param ComponentCollection $collection The Component collection used on this request. + * @param array $settings An array of settings. + */ + public function __construct(ComponentCollection $collection, $settings) { + parent::__construct($collection, $settings); + if (empty($this->settings['realm'])) { + $this->settings['realm'] = env('SERVER_NAME'); + } + if (empty($this->settings['nonce'])) { + $this->settings['nonce'] = uniqid(''); + } + if (empty($this->settings['opaque'])) { + $this->settings['opaque'] = md5($this->settings['realm']); + } + } +/** + * Authenticate a user using Digest HTTP auth. Will use the configured User model and attempt a + * login using Digest HTTP auth. + * + * @param CakeRequest $request The request to authenticate with. + * @param CakeResponse $response The response to add headers to. + * @return mixed Either false on failure, or an array of user data on success. + */ + public function authenticate(CakeRequest $request, CakeResponse $response) { + $user = $this->getUser($request); + + if (empty($user)) { + $response->header($this->loginHeaders()); + $response->statusCode(401); + $response->send(); + return false; + } + return $user; + } + +/** + * Get a user based on information in the request. Used by cookie-less auth for stateless clients. + * + * @param CakeRequest $request Request object. + * @return mixed Either false or an array of user information + */ + public function getUser($request) { + $digest = $this->_getDigest(); + if (empty($digest)) { + return false; + } + $user = $this->_findUser($digest['username'], null); + if (empty($user)) { + return false; + } + $password = $user[$this->settings['fields']['password']]; + unset($user[$this->settings['fields']['password']]); + if ($digest['response'] === $this->generateResponseHash($digest, $password)) { + return $user; + } + return false; + } +/** + * Find a user record using the standard options. + * + * @param string $username The username/identifier. + * @param string $password Unused password, digest doesn't require passwords. + * @return Mixed Either false on failure, or an array of user data. + */ + protected function _findUser($username, $password) { + $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + $fields = $this->settings['fields']; + + $conditions = array( + $model . '.' . $fields['username'] => $username, + ); + if (!empty($this->settings['scope'])) { + $conditions = array_merge($conditions, $this->settings['scope']); + } + $result = ClassRegistry::init($userModel)->find('first', array( + 'conditions' => $conditions, + 'recursive' => 0 + )); + if (empty($result) || empty($result[$model])) { + return false; + } + return $result[$model]; + } + +/** + * Gets the digest headers from the request/environment. + * + * @return array Array of digest information. + */ + protected function _getDigest() { + $digest = env('PHP_AUTH_DIGEST'); + if (empty($digest) && function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + if (!empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') { + $digest = substr($headers['Authorization'], 7); + } + } + if (empty($digest)) { + return false; + } + return $this->parseAuthData($digest); + } + +/** + * Parse the digest authentication headers and split them up. + * + * @param string $digest The raw digest authentication headers. + * @return array An array of digest authentication headers + */ + public function parseAuthData($digest) { + if (substr($digest, 0, 7) == 'Digest ') { + $digest = substr($digest, 7); + } + $keys = $match = array(); + $req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1); + preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER); + + foreach ($match as $i) { + $keys[$i[1]] = $i[3]; + unset($req[$i[1]]); + } + + if (empty($req)) { + return $keys; + } + return null; + } + +/** + * Generate the response hash for a given digest array. + * + * @param array $digest Digest information containing data from DigestAuthenticate::parseAuthData(). + * @param string $password The digest hash password generated with DigestAuthenticate::password() + * @return string Response hash + */ + public function generateResponseHash($digest, $password) { + return md5( + $password . + ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . + md5(env('REQUEST_METHOD') . ':' . $digest['uri']) + ); + } + +/** + * Creates an auth digest password hash to store + * + * @param string $username The username to use in the digest hash. + * @param string $password The unhashed password to make a digest hash for. + * @param string $realm The realm the password is for. + * @return string the hashed password that can later be used with Digest authentication. + */ + public static function password($username, $password, $realm) { + return md5($username . ':' . $realm . ':' . $password); + } + +/** + * Generate the login headers + * + * @return string Headers for logging in. + */ + public function loginHeaders() { + $options = array( + 'realm' => $this->settings['realm'], + 'qop' => $this->settings['qop'], + 'nonce' => $this->settings['nonce'], + 'opaque' => $this->settings['opaque'] + ); + $opts = array(); + foreach ($options as $k => $v) { + $opts[] = sprintf('%s="%s"', $k, $v); + } + return 'WWW-Authenticate: Digest ' . implode(',', $opts); + } +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/Auth/FormAuthenticate.php b/app/Cake/Controller/Component/Auth/FormAuthenticate.php new file mode 100644 index 00000000..833b4d68 --- /dev/null +++ b/app/Cake/Controller/Component/Auth/FormAuthenticate.php @@ -0,0 +1,68 @@ +Auth->authenticate = array( + * 'Form' => array( + * 'scope' => array('User.active' => 1) + * ) + * ) + * }}} + * + * When configuring FormAuthenticate you can pass in settings to which fields, model and additional conditions + * are used. See FormAuthenticate::$settings for more information. + * + * @package Cake.Controller.Component.Auth + * @since 2.0 + * @see AuthComponent::$authenticate + */ +class FormAuthenticate extends BaseAuthenticate { + +/** + * Authenticates the identity contained in a request. Will use the `settings.userModel`, and `settings.fields` + * to find POST data that is used to find a matching record in the `settings.userModel`. Will return false if + * there is no post data, either username or password is missing, of if the scope conditions have not been met. + * + * @param CakeRequest $request The request that contains login information. + * @param CakeResponse $response Unused response object. + * @return mixed. False on login failure. An array of User data on success. + */ + public function authenticate(CakeRequest $request, CakeResponse $response) { + $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + + $fields = $this->settings['fields']; + if (empty($request->data[$model])) { + return false; + } + if ( + empty($request->data[$model][$fields['username']]) || + empty($request->data[$model][$fields['password']]) + ) { + return false; + } + return $this->_findUser( + $request->data[$model][$fields['username']], + $request->data[$model][$fields['password']] + ); + } + +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/AuthComponent.php b/app/Cake/Controller/Component/AuthComponent.php new file mode 100644 index 00000000..deef4bc7 --- /dev/null +++ b/app/Cake/Controller/Component/AuthComponent.php @@ -0,0 +1,702 @@ +Auth->authenticate = array( + * 'Form' => array( + * 'userModel' => 'Users.User' + * ) + * ); + * }}} + * + * Using the class name without 'Authenticate' as the key, you can pass in an array of settings for each + * authentication object. Additionally you can define settings that should be set to all authentications objects + * using the 'all' key: + * + * {{{ + * $this->Auth->authenticate = array( + * 'all' => array( + * 'userModel' => 'Users.User', + * 'scope' => array('User.active' => 1) + * ), + * 'Form', + * 'Basic' + * ); + * }}} + * + * You can also use AuthComponent::ALL instead of the string 'all'. + * + * @var array + * @link http://book.cakephp.org/view/1278/authenticate + */ + public $authenticate = array('Form'); + +/** + * Objects that will be used for authentication checks. + * + * @var array + */ + protected $_authenticateObjects = array(); + +/** + * An array of authorization objects to use for authorizing users. You can configure + * multiple adapters and they will be checked sequentially when authorization checks are done. + * + * {{{ + * $this->Auth->authorize = array( + * 'Crud' => array( + * 'actionPath' => 'controllers/' + * ) + * ); + * }}} + * + * Using the class name without 'Authorize' as the key, you can pass in an array of settings for each + * authorization object. Additionally you can define settings that should be set to all authorization objects + * using the 'all' key: + * + * {{{ + * $this->Auth->authorize = array( + * 'all' => array( + * 'actionPath' => 'controllers/' + * ), + * 'Crud', + * 'CustomAuth' + * ); + * }}} + * + * You can also use AuthComponent::ALL instead of the string 'all' + * + * @var mixed + * @link http://book.cakephp.org/view/1275/authorize + */ + public $authorize = false; + +/** + * Objects that will be used for authorization checks. + * + * @var array + */ + protected $_authorizeObjects = array(); + +/** + * The name of an optional view element to render when an Ajax request is made + * with an invalid or expired session + * + * @var string + * @link http://book.cakephp.org/view/1277/ajaxLogin + */ + public $ajaxLogin = null; + +/** + * Settings to use when Auth needs to do a flash message with SessionComponent::setFlash(). + * Available keys are: + * + * - `element` - The element to use, defaults to 'default'. + * - `key` - The key to use, defaults to 'auth' + * - `params` - The array of additional params to use, defaults to array() + * + * @var array + */ + public $flash = array( + 'element' => 'default', + 'key' => 'auth', + 'params' => array() + ); + +/** + * The session key name where the record of the current user is stored. If + * unspecified, it will be "Auth.User". + * + * @var string + * @link http://book.cakephp.org/view/1276/sessionKey + */ + public static $sessionKey = 'Auth.User'; + +/** + * A URL (defined as a string or array) to the controller action that handles + * logins. Defaults to `/users/login` + * + * @var mixed + * @link http://book.cakephp.org/view/1269/loginAction + */ + public $loginAction = array( + 'controller' => 'users', + 'action' => 'login', + 'plugin' => null + ); + +/** + * Normally, if a user is redirected to the $loginAction page, the location they + * were redirected from will be stored in the session so that they can be + * redirected back after a successful login. If this session value is not + * set, the user will be redirected to the page specified in $loginRedirect. + * + * @var mixed + * @link http://book.cakephp.org/view/1270/loginRedirect + */ + public $loginRedirect = null; + +/** + * The default action to redirect to after the user is logged out. While AuthComponent does + * not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout(). + * Defaults to AuthComponent::$loginAction. + * + * @var mixed + * @see AuthComponent::$loginAction + * @see AuthComponent::logout() + * @link http://book.cakephp.org/view/1271/logoutRedirect + */ + public $logoutRedirect = null; + +/** + * Error to display when user attempts to access an object or action to which they do not have + * acccess. + * + * @var string + * @link http://book.cakephp.org/view/1273/authError + */ + public $authError = null; + +/** + * Controller actions for which user validation is not required. + * + * @var array + * @see AuthComponent::allow() + * @link http://book.cakephp.org/view/1251/Setting-Auth-Component-Variables + */ + public $allowedActions = array(); + +/** + * Request object + * + * @var CakeRequest + */ + public $request; + +/** + * Response object + * + * @var CakeResponse + */ + public $response; + +/** + * Method list for bound controller + * + * @var array + */ + protected $_methods = array(); + +/** + * Initializes AuthComponent for use in the controller + * + * @param Controller $controller A reference to the instantiating controller object + * @return void + */ + public function initialize($controller) { + $this->request = $controller->request; + $this->response = $controller->response; + $this->_methods = $controller->methods; + + if (Configure::read('debug') > 0) { + Debugger::checkSecurityKeys(); + } + } + +/** + * Main execution method. Handles redirecting of invalid users, and processing + * of login form data. + * + * @param Controller $controller A reference to the instantiating controller object + * @return boolean + */ + public function startup($controller) { + if ($controller->name == 'CakeError') { + return true; + } + + $methods = array_flip($controller->methods); + $action = $controller->request->params['action']; + + $isMissingAction = ( + $controller->scaffold === false && + !isset($methods[$action]) + ); + + if ($isMissingAction) { + return true; + } + + if (!$this->_setDefaults()) { + return false; + } + $request = $controller->request; + + $url = ''; + + if (isset($request->url)) { + $url = $request->url; + } + $url = Router::normalize($url); + $loginAction = Router::normalize($this->loginAction); + + $allowedActions = $this->allowedActions; + $isAllowed = ( + $this->allowedActions == array('*') || + in_array($action, $allowedActions) + ); + + if ($loginAction != $url && $isAllowed) { + return true; + } + + if ($loginAction == $url) { + if (empty($request->data)) { + if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) { + $this->Session->write('Auth.redirect', $controller->referer(null, true)); + } + } + return true; + } else { + if (!$this->_getUser()) { + if (!$request->is('ajax')) { + $this->flash($this->authError); + $this->Session->write('Auth.redirect', Router::reverse($request)); + $controller->redirect($loginAction); + return false; + } elseif (!empty($this->ajaxLogin)) { + $controller->viewPath = 'Elements'; + echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout); + $this->_stop(); + return false; + } else { + $controller->redirect(null, 403); + } + } + } + if (empty($this->authorize) || $this->isAuthorized($this->user())) { + return true; + } + + $this->flash($this->authError); + $controller->redirect($controller->referer('/'), null, true); + return false; + } + +/** + * Attempts to introspect the correct values for object properties including + * $userModel and $sessionKey. + * + * @return boolean + */ + protected function _setDefaults() { + $defaults = array( + 'logoutRedirect' => $this->loginAction, + 'authError' => __d('cake', 'You are not authorized to access that location.') + ); + foreach ($defaults as $key => $value) { + if (empty($this->{$key})) { + $this->{$key} = $value; + } + } + return true; + } + +/** + * Uses the configured Authorization adapters to check whether or not a user is authorized. + * Each adapter will be checked in sequence, if any of them return true, then the user will + * be authorized for the request. + * + * @param mixed $user The user to check the authorization of. If empty the user in the session will be used. + * @param CakeRequest $request The request to authenticate for. If empty, the current request will be used. + * @return boolean True if $user is authorized, otherwise false + */ + public function isAuthorized($user = null, $request = null) { + if (empty($user) && !$this->user()) { + return false; + } elseif (empty($user)) { + $user = $this->user(); + } + if (empty($request)) { + $request = $this->request; + } + if (empty($this->_authorizeObjects)) { + $this->constructAuthorize(); + } + foreach ($this->_authorizeObjects as $authorizer) { + if ($authorizer->authorize($user, $request) === true) { + return true; + } + } + return false; + } + +/** + * Loads the authorization objects configured. + * + * @return mixed Either null when authorize is empty, or the loaded authorization objects. + * @throws CakeException + */ + public function constructAuthorize() { + if (empty($this->authorize)) { + return; + } + $this->_authorizeObjects = array(); + $config = Set::normalize($this->authorize); + $global = array(); + if (isset($config[AuthComponent::ALL])) { + $global = $config[AuthComponent::ALL]; + unset($config[AuthComponent::ALL]); + } + foreach ($config as $class => $settings) { + list($plugin, $class) = pluginSplit($class, true); + $className = $class . 'Authorize'; + App::uses($className, $plugin . 'Controller/Component/Auth'); + if (!class_exists($className)) { + throw new CakeException(__d('cake_dev', 'Authorization adapter "%s" was not found.', $class)); + } + if (!method_exists($className, 'authorize')) { + throw new CakeException(__d('cake_dev', 'Authorization objects must implement an authorize method.')); + } + $settings = array_merge($global, (array)$settings); + $this->_authorizeObjects[] = new $className($this->_Collection, $settings); + } + return $this->_authorizeObjects; + } + +/** + * Takes a list of actions in the current controller for which authentication is not required, or + * no parameters to allow all actions. + * + * You can use allow with either an array, or var args. + * + * `$this->Auth->allow(array('edit', 'add'));` or + * `$this->Auth->allow('edit', 'add');` + * + * allow() also supports '*' as a wildcard to mean all actions. + * + * `$this->Auth->allow('*');` + * + * @param mixed $action,... Controller action name or array of actions + * @return void + * @link http://book.cakephp.org/view/1257/allow + */ + public function allow($action = null) { + $args = func_get_args(); + if (empty($args) || $args == array('*')) { + $this->allowedActions = $this->_methods; + } else { + if (isset($args[0]) && is_array($args[0])) { + $args = $args[0]; + } + $this->allowedActions = array_merge($this->allowedActions, $args); + } + } + +/** + * Removes items from the list of allowed/no authentication required actions. + * + * You can use deny with either an array, or var args. + * + * `$this->Auth->deny(array('edit', 'add'));` or + * `$this->Auth->deny('edit', 'add');` + * + * @param mixed $action,... Controller action name or array of actions + * @return void + * @see AuthComponent::allow() + * @link http://book.cakephp.org/view/1258/deny + */ + public function deny($action = null) { + $args = func_get_args(); + if (isset($args[0]) && is_array($args[0])) { + $args = $args[0]; + } + foreach ($args as $arg) { + $i = array_search($arg, $this->allowedActions); + if (is_int($i)) { + unset($this->allowedActions[$i]); + } + } + $this->allowedActions = array_values($this->allowedActions); + } + +/** + * Maps action names to CRUD operations. Used for controller-based authentication. Make sure + * to configure the authorize property before calling this method. As it delegates $map to all the + * attached authorize objects. + * + * @param array $map Actions to map + * @return void + * @link http://book.cakephp.org/view/1260/mapActions + */ + public function mapActions($map = array()) { + if (empty($this->_authorizeObjects)) { + $this->constructAuthorize(); + } + foreach ($this->_authorizeObjects as $auth) { + $auth->mapActions($map); + } + } + +/** + * Log a user in. If a $user is provided that data will be stored as the logged in user. If `$user` is empty or not + * specified, the request will be used to identify a user. If the identification was successful, + * the user record is written to the session key specified in AuthComponent::$sessionKey. + * + * @param mixed $user Either an array of user data, or null to identify a user using the current request. + * @return boolean True on login success, false on failure + * @link http://book.cakephp.org/view/1261/login + */ + public function login($user = null) { + $this->_setDefaults(); + + if (empty($user)) { + $user = $this->identify($this->request, $this->response); + } + if ($user) { + $this->Session->write(self::$sessionKey, $user); + } + return $this->loggedIn(); + } + +/** + * Logs a user out, and returns the login action to redirect to. + * Triggers the logout() method of all the authenticate objects, so they can perform + * custom logout logic. AuthComponent will remove the session data, so + * there is no need to do that in an authentication object. + * + * @return string AuthComponent::$logoutRedirect + * @see AuthComponent::$logoutRedirect + * @link http://book.cakephp.org/view/1262/logout + */ + public function logout() { + $this->_setDefaults(); + if (empty($this->_authenticateObjects)) { + $this->constructAuthenticate(); + } + $user = $this->user(); + foreach ($this->_authenticateObjects as $auth) { + $auth->logout($user); + } + $this->Session->delete(self::$sessionKey); + $this->Session->delete('Auth.redirect'); + return Router::normalize($this->logoutRedirect); + } + +/** + * Get the current user from the session. + * + * @param string $key field to retrive. Leave null to get entire User record + * @return mixed User record. or null if no user is logged in. + * @link http://book.cakephp.org/view/1264/user + */ + public static function user($key = null) { + if (!CakeSession::check(self::$sessionKey)) { + return null; + } + + if ($key == null) { + return CakeSession::read(self::$sessionKey); + } + + $user = CakeSession::read(self::$sessionKey); + if (isset($user[$key])) { + return $user[$key]; + } + return null; + } + +/** + * Similar to AuthComponent::user() except if the session user cannot be found, connected authentication + * objects will have their getUser() methods called. This lets stateless authentication methods function correctly. + * + * @return boolean true if a user can be found, false if one cannot. + */ + protected function _getUser() { + $user = $this->user(); + if ($user) { + return true; + } + if (empty($this->_authenticateObjects)) { + $this->constructAuthenticate(); + } + foreach ($this->_authenticateObjects as $auth) { + $result = $auth->getUser($this->request); + if (!empty($result) && is_array($result)) { + return true; + } + } + return false; + } + +/** + * If no parameter is passed, gets the authentication redirect URL. Pass a url in to + * set the destination a user should be redirected to upon logging in. Will fallback to + * AuthComponent::$loginRedirect if there is no stored redirect value. + * + * @param mixed $url Optional URL to write as the login redirect URL. + * @return string Redirect URL + */ + public function redirect($url = null) { + if (!is_null($url)) { + $redir = $url; + $this->Session->write('Auth.redirect', $redir); + } elseif ($this->Session->check('Auth.redirect')) { + $redir = $this->Session->read('Auth.redirect'); + $this->Session->delete('Auth.redirect'); + + if (Router::normalize($redir) == Router::normalize($this->loginAction)) { + $redir = $this->loginRedirect; + } + } else { + $redir = $this->loginRedirect; + } + return Router::normalize($redir); + } + +/** + * Use the configured authentication adapters, and attempt to identify the user + * by credentials contained in $request. + * + * @param CakeRequest $request The request that contains authentication data. + * @param CakeResponse $response The response + * @return array User record data, or false, if the user could not be identified. + */ + public function identify(CakeRequest $request, CakeResponse $response) { + if (empty($this->_authenticateObjects)) { + $this->constructAuthenticate(); + } + foreach ($this->_authenticateObjects as $auth) { + $result = $auth->authenticate($request, $response); + if (!empty($result) && is_array($result)) { + return $result; + } + } + return false; + } + +/** + * loads the configured authentication objects. + * + * @return mixed either null on empty authenticate value, or an array of loaded objects. + * @throws CakeException + */ + public function constructAuthenticate() { + if (empty($this->authenticate)) { + return; + } + $this->_authenticateObjects = array(); + $config = Set::normalize($this->authenticate); + $global = array(); + if (isset($config[AuthComponent::ALL])) { + $global = $config[AuthComponent::ALL]; + unset($config[AuthComponent::ALL]); + } + foreach ($config as $class => $settings) { + list($plugin, $class) = pluginSplit($class, true); + $className = $class . 'Authenticate'; + App::uses($className, $plugin . 'Controller/Component/Auth'); + if (!class_exists($className)) { + throw new CakeException(__d('cake_dev', 'Authentication adapter "%s" was not found.', $class)); + } + if (!method_exists($className, 'authenticate')) { + throw new CakeException(__d('cake_dev', 'Authentication objects must implement an authenticate method.')); + } + $settings = array_merge($global, (array)$settings); + $this->_authenticateObjects[] = new $className($this->_Collection, $settings); + } + return $this->_authenticateObjects; + } + +/** + * Hash a password with the application's salt value (as defined with Configure::write('Security.salt'); + * + * @param string $password Password to hash + * @return string Hashed password + * @link http://book.cakephp.org/view/1263/password + */ + public static function password($password) { + return Security::hash($password, null, true); + } + +/** + * Component shutdown. If user is logged in, wipe out redirect. + * + * @param Controller $controller Instantiating controller + * @return void + */ + public function shutdown($controller) { + if ($this->loggedIn()) { + $this->Session->delete('Auth.redirect'); + } + } + +/** + * Check whether or not the current user has data in the session, and is considered logged in. + * + * @return boolean true if the user is logged in, false otherwise + */ + public function loggedIn() { + return $this->user() != array(); + } + +/** + * Set a flash message. Uses the Session component, and values from AuthComponent::$flash. + * + * @param string $message The message to set. + * @return void + */ + public function flash($message) { + $this->Session->setFlash($message, $this->flash['element'], $this->flash['params'], $this->flash['key']); + } +} diff --git a/app/Cake/Controller/Component/CookieComponent.php b/app/Cake/Controller/Component/CookieComponent.php new file mode 100644 index 00000000..c20d5729 --- /dev/null +++ b/app/Cake/Controller/Component/CookieComponent.php @@ -0,0 +1,495 @@ +Cookie->name = 'CookieName'; + * + * @var string + */ + public $name = 'CakeCookie'; + +/** + * The time a cookie will remain valid. + * + * Can be either integer Unix timestamp or a date string. + * + * Overridden with the controller beforeFilter(); + * $this->Cookie->time = '5 Days'; + * + * @var mixed + */ + public $time = null; + +/** + * Cookie path. + * + * Overridden with the controller beforeFilter(); + * $this->Cookie->path = '/'; + * + * The path on the server in which the cookie will be available on. + * If public $cookiePath is set to '/foo/', the cookie will only be available + * within the /foo/ directory and all sub-directories such as /foo/bar/ of domain. + * The default value is the entire domain. + * + * @var string + */ + public $path = '/'; + +/** + * Domain path. + * + * The domain that the cookie is available. + * + * Overridden with the controller beforeFilter(); + * $this->Cookie->domain = '.example.com'; + * + * To make the cookie available on all subdomains of example.com. + * Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter + * + * @var string + */ + public $domain = ''; + +/** + * Secure HTTPS only cookie. + * + * Overridden with the controller beforeFilter(); + * $this->Cookie->secure = true; + * + * Indicates that the cookie should only be transmitted over a secure HTTPS connection. + * When set to true, the cookie will only be set if a secure connection exists. + * + * @var boolean + */ + public $secure = false; + +/** + * Encryption key. + * + * Overridden with the controller beforeFilter(); + * $this->Cookie->key = 'SomeRandomString'; + * + * @var string + */ + public $key = null; + +/** + * HTTP only cookie + * + * Set to true to make HTTP only cookies. Cookies that are HTTP only + * are not accessible in Javascript. + * + * @var boolean + */ + public $httpOnly = false; + +/** + * Values stored in the cookie. + * + * Accessed in the controller using $this->Cookie->read('Name.key'); + * + * @see CookieComponent::read(); + * @var string + */ + protected $_values = array(); + +/** + * Type of encryption to use. + * + * Currently only one method is available + * Defaults to Security::cipher(); + * + * @var string + * @todo add additional encryption methods + */ + protected $_type = 'cipher'; + +/** + * Used to reset cookie time if $expire is passed to CookieComponent::write() + * + * @var string + */ + protected $_reset = null; + +/** + * Expire time of the cookie + * + * This is controlled by CookieComponent::time; + * + * @var string + */ + protected $_expires = 0; + +/** + * Constructor + * + * @param ComponentCollection $collection A ComponentCollection for this component + * @param array $settings Array of settings. + */ + public function __construct(ComponentCollection $collection, $settings = array()) { + $this->key = Configure::read('Security.salt'); + parent::__construct($collection, $settings); + if (isset($this->time)) { + $this->_expire($this->time); + } + } + +/** + * Start CookieComponent for use in the controller + * + * @param Controller $controller + * @return void + */ + public function startup($controller) { + $this->_expire($this->time); + + if (isset($_COOKIE[$this->name])) { + $this->_values = $this->_decrypt($_COOKIE[$this->name]); + } + } + +/** + * Write a value to the $_COOKIE[$key]; + * + * Optional [Name.], required key, optional $value, optional $encrypt, optional $expires + * $this->Cookie->write('[Name.]key, $value); + * + * By default all values are encrypted. + * You must pass $encrypt false to store values in clear test + * + * You must use this method before any output is sent to the browser. + * Failure to do so will result in header already sent errors. + * + * @param mixed $key Key for the value + * @param mixed $value Value + * @param boolean $encrypt Set to true to encrypt value, false otherwise + * @param string $expires Can be either Unix timestamp, or date string + * @return void + */ + public function write($key, $value = null, $encrypt = true, $expires = null) { + if (is_null($encrypt)) { + $encrypt = true; + } + $this->_encrypted = $encrypt; + $this->_expire($expires); + + if (!is_array($key)) { + $key = array($key => $value); + } + + foreach ($key as $name => $value) { + if (strpos($name, '.') === false) { + $this->_values[$name] = $value; + $this->_write("[$name]", $value); + } else { + $names = explode('.', $name, 2); + if (!isset($this->_values[$names[0]])) { + $this->_values[$names[0]] = array(); + } + $this->_values[$names[0]] = Set::insert($this->_values[$names[0]], $names[1], $value); + $this->_write('[' . implode('][', $names) . ']', $value); + } + } + $this->_encrypted = true; + } + +/** + * Read the value of the $_COOKIE[$key]; + * + * Optional [Name.], required key + * $this->Cookie->read(Name.key); + * + * @param mixed $key Key of the value to be obtained. If none specified, obtain map key => values + * @return string or null, value for specified key + */ + public function read($key = null) { + if (empty($this->_values) && isset($_COOKIE[$this->name])) { + $this->_values = $this->_decrypt($_COOKIE[$this->name]); + } + + if (is_null($key)) { + return $this->_values; + } + + if (strpos($key, '.') !== false) { + $names = explode('.', $key, 2); + $key = $names[0]; + } + if (!isset($this->_values[$key])) { + return null; + } + + if (!empty($names[1])) { + return Set::extract($this->_values[$key], $names[1]); + } + return $this->_values[$key]; + } + +/** + * Delete a cookie value + * + * Optional [Name.], required key + * $this->Cookie->read('Name.key); + * + * You must use this method before any output is sent to the browser. + * Failure to do so will result in header already sent errors. + * + * @param string $key Key of the value to be deleted + * @return void + */ + public function delete($key) { + if (empty($this->_values)) { + $this->read(); + } + if (strpos($key, '.') === false) { + if (isset($this->_values[$key]) && is_array($this->_values[$key])) { + foreach ($this->_values[$key] as $idx => $val) { + $this->_delete("[$key][$idx]"); + } + } + $this->_delete("[$key]"); + unset($this->_values[$key]); + return; + } + $names = explode('.', $key, 2); + if (isset($this->_values[$names[0]])) { + $this->_values[$names[0]] = Set::remove($this->_values[$names[0]], $names[1]); + } + $this->_delete('[' . implode('][', $names) . ']'); + } + +/** + * Destroy current cookie + * + * You must use this method before any output is sent to the browser. + * Failure to do so will result in header already sent errors. + * + * @return void + */ + public function destroy() { + if (isset($_COOKIE[$this->name])) { + $this->_values = $this->_decrypt($_COOKIE[$this->name]); + } + + foreach ($this->_values as $name => $value) { + if (is_array($value)) { + foreach ($value as $key => $val) { + unset($this->_values[$name][$key]); + $this->_delete("[$name][$key]"); + } + } + unset($this->_values[$name]); + $this->_delete("[$name]"); + } + } + +/** + * Will allow overriding default encryption method. + * + * @param string $type Encryption method + * @return void + * @todo NOT IMPLEMENTED + */ + public function type($type = 'cipher') { + $this->_type = 'cipher'; + } + +/** + * Set the expire time for a session variable. + * + * Creates a new expire time for a session variable. + * $expire can be either integer Unix timestamp or a date string. + * + * Used by write() + * CookieComponent::write(string, string, boolean, 8400); + * CookieComponent::write(string, string, boolean, '5 Days'); + * + * @param mixed $expires Can be either Unix timestamp, or date string + * @return integer Unix timestamp + */ + protected function _expire($expires = null) { + $now = time(); + if (is_null($expires)) { + return $this->_expires; + } + $this->_reset = $this->_expires; + + if ($expires == 0) { + return $this->_expires = 0; + } + + if (is_integer($expires) || is_numeric($expires)) { + return $this->_expires = $now + intval($expires); + } + return $this->_expires = strtotime($expires, $now); + } + +/** + * Set cookie + * + * @param string $name Name for cookie + * @param string $value Value for cookie + * @return void + */ + protected function _write($name, $value) { + $this->_setcookie( + $this->name . $name, $this->_encrypt($value), + $this->_expires, $this->path, $this->domain, $this->secure, $this->httpOnly + ); + + if (!is_null($this->_reset)) { + $this->_expires = $this->_reset; + $this->_reset = null; + } + } + +/** + * Sets a cookie expire time to remove cookie value + * + * @param string $name Name of cookie + * @return void + */ + protected function _delete($name) { + $this->_setcookie( + $this->name . $name, '', + time() - 42000, $this->path, $this->domain, $this->secure, $this->httpOnly + ); + } + +/** + * Object wrapper for setcookie() so it can be mocked in unit tests. + * + * @todo Re-factor setting cookies into CakeResponse. Cookies are part + * of the HTTP response, and should be handled there. + * + * @param string $name Name of the cookie + * @param string $value Value of the cookie + * @param integer $expire Time the cookie expires in + * @param string $path Path the cookie applies to + * @param string $domain Domain the cookie is for. + * @param boolean $secure Is the cookie https? + * @param boolean $httpOnly Is the cookie available in the client? + * @return void + */ + protected function _setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly = false) { + setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); + } +/** + * Encrypts $value using public $type method in Security class + * + * @param string $value Value to encrypt + * @return string encrypted string + * @return string Encoded values + */ + protected function _encrypt($value) { + if (is_array($value)) { + $value = $this->_implode($value); + } + + if ($this->_encrypted === true) { + $type = $this->_type; + $value = "Q2FrZQ==." .base64_encode(Security::$type($value, $this->key)); + } + return $value; + } + +/** + * Decrypts $value using public $type method in Security class + * + * @param array $values Values to decrypt + * @return string decrypted string + */ + protected function _decrypt($values) { + $decrypted = array(); + $type = $this->_type; + + foreach ((array)$values as $name => $value) { + if (is_array($value)) { + foreach ($value as $key => $val) { + $pos = strpos($val, 'Q2FrZQ==.'); + $decrypted[$name][$key] = $this->_explode($val); + + if ($pos !== false) { + $val = substr($val, 8); + $decrypted[$name][$key] = $this->_explode(Security::$type(base64_decode($val), $this->key)); + } + } + } else { + $pos = strpos($value, 'Q2FrZQ==.'); + $decrypted[$name] = $this->_explode($value); + + if ($pos !== false) { + $value = substr($value, 8); + $decrypted[$name] = $this->_explode(Security::$type(base64_decode($value), $this->key)); + } + } + } + return $decrypted; + } + +/** + * Implode method to keep keys are multidimensional arrays + * + * @param array $array Map of key and values + * @return string A json encoded string. + */ + protected function _implode(array $array) { + return json_encode($array); + } + +/** + * Explode method to return array from string set in CookieComponent::_implode() + * Maintains reading backwards compatibility with 1.x CookieComponent::_implode(). + * + * @param string $string A string containing JSON encoded data, or a bare string. + * @return array Map of key and values + */ + protected function _explode($string) { + if ($string[0] === '{' || $string[0] === '[') { + $ret = json_decode($string, true); + return ($ret != null) ? $ret : $string; + } + $array = array(); + foreach (explode(',', $string) as $pair) { + $key = explode('|', $pair); + if (!isset($key[1])) { + return $key[0]; + } + $array[$key[0]] = $key[1]; + } + return $array; + } +} diff --git a/app/Cake/Controller/Component/EmailComponent.php b/app/Cake/Controller/Component/EmailComponent.php new file mode 100644 index 00000000..de23ea36 --- /dev/null +++ b/app/Cake/Controller/Component/EmailComponent.php @@ -0,0 +1,489 @@ +_controller = $collection->getController(); + parent::__construct($collection, $settings); + } + +/** + * Initialize component + * + * @param Controller $controller Instantiating controller + * @return void + */ + public function initialize($controller) { + if (Configure::read('App.encoding') !== null) { + $this->charset = Configure::read('App.encoding'); + } + } + +/** + * Send an email using the specified content, template and layout + * + * @param mixed $content Either an array of text lines, or a string with contents + * If you are rendering a template this variable will be sent to the templates as `$content` + * @param string $template Template to use when sending email + * @param string $layout Layout to use to enclose email body + * @return boolean Success + */ + public function send($content = null, $template = null, $layout = null) { + $lib = new CakeEmail(); + $lib->charset = $this->charset; + + $lib->from($this->_formatAddresses((array)$this->from)); + if (!empty($this->to)) { + $lib->to($this->_formatAddresses((array)$this->to)); + } + if (!empty($this->cc)) { + $lib->cc($this->_formatAddresses((array)$this->cc)); + } + if (!empty($this->bcc)) { + $lib->bcc($this->_formatAddresses((array)$this->bcc)); + } + if (!empty($this->replyTo)) { + $lib->replyTo($this->_formatAddresses((array)$this->replyTo)); + } + if (!empty($this->return)) { + $lib->returnPath($this->_formatAddresses((array)$this->return)); + } + if (!empty($readReceipt)) { + $lib->readReceipt($this->_formatAddresses((array)$this->readReceipt)); + } + + $lib->subject($this->subject)->messageID($this->messageId); + $lib->helpers($this->_controller->helpers); + + $headers = array('X-Mailer' => $this->xMailer); + foreach ($this->headers as $key => $value) { + $headers['X-' . $key] = $value; + } + if ($this->date != false) { + $headers['Date'] = $this->date; + } + $lib->setHeaders($headers); + + if ($template) { + $this->template = $template; + } + if ($layout) { + $this->layout = $layout; + } + $lib->template($this->template, $this->layout)->viewVars($this->_controller->viewVars)->emailFormat($this->sendAs); + + if (!empty($this->attachments)) { + $lib->attachments($this->_formatAttachFiles()); + } + + $lib->transport(ucfirst($this->delivery)); + if ($this->delivery === 'mail') { + $lib->config(array('eol' => $this->lineFeed, 'additionalParameters' => $this->additionalParams)); + } elseif ($this->delivery === 'smtp') { + $lib->config($this->smtpOptions); + } else { + $lib->config(array()); + } + + $sent = $lib->send($content); + + $this->htmlMessage = $lib->message(CakeEmail::MESSAGE_HTML); + if (empty($this->htmlMessage)) { + $this->htmlMessage = null; + } + $this->textMessage = $lib->message(CakeEmail::MESSAGE_TEXT); + if (empty($this->textMessage)) { + $this->textMessage = null; + } + + $this->_header = array(); + $this->_message = array(); + + return $sent; + } + +/** + * Reset all EmailComponent internal variables to be able to send out a new email. + * + * @return void + * @link http://book.cakephp.org/view/1285/Sending-Multiple-Emails-in-a-loop + */ + public function reset() { + $this->template = null; + $this->to = array(); + $this->from = null; + $this->replyTo = null; + $this->return = null; + $this->cc = array(); + $this->bcc = array(); + $this->subject = null; + $this->additionalParams = null; + $this->date = null; + $this->attachments = array(); + $this->htmlMessage = null; + $this->textMessage = null; + $this->messageId = true; + } + +/** + * Format the attach array + * + * @return array + */ + protected function _formatAttachFiles() { + $files = array(); + foreach ($this->attachments as $filename => $attachment) { + $file = $this->_findFiles($attachment); + if (!empty($file)) { + if (is_int($filename)) { + $filename = basename($file); + } + $files[$filename] = $file; + } + } + return $files; + } + +/** + * Find the specified attachment in the list of file paths + * + * @param string $attachment Attachment file name to find + * @return string Path to located file + */ + protected function _findFiles($attachment) { + if (file_exists($attachment)) { + return $attachment; + } + foreach ($this->filePaths as $path) { + if (file_exists($path . DS . $attachment)) { + $file = $path . DS . $attachment; + return $file; + } + } + return null; + } + +/** + * Encode the specified string using the current charset + * + * @param string $subject String to encode + * @return string Encoded string + */ + protected function _encode($subject) { + $subject = $this->_strip($subject); + + $nl = "\r\n"; + if ($this->delivery == 'mail') { + $nl = ''; + } + $internalEncoding = function_exists('mb_internal_encoding'); + if ($internalEncoding) { + $restore = mb_internal_encoding(); + mb_internal_encoding($this->charset); + } + $return = mb_encode_mimeheader($subject, $this->charset, 'B', $nl); + if ($internalEncoding) { + mb_internal_encoding($restore); + } + return $return; + } + +/** + * Format addresses to be an array with email as key and alias as value + * + * @param array $addresses + * @return array + */ + protected function _formatAddresses($addresses) { + $formatted = array(); + foreach ($addresses as $address) { + if (preg_match('/((.*))?\s?<(.+)>/', $address, $matches) && !empty($matches[2])) { + $formatted[$this->_strip($matches[3])] = $this->_encode($matches[2]); + } else { + $address = $this->_strip($address); + $formatted[$address] = $address; + } + } + return $formatted; + } + +/** + * Remove certain elements (such as bcc:, to:, %0a) from given value. + * Helps prevent header injection / mainipulation on user content. + * + * @param string $value Value to strip + * @param boolean $message Set to true to indicate main message content + * @return string Stripped value + */ + protected function _strip($value, $message = false) { + $search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:'; + $search .= '|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*'; + + if ($message !== true) { + $search .= '|\r|\n'; + } + $search = '#(?:' . $search . ')#i'; + while (preg_match($search, $value)) { + $value = preg_replace($search, '', $value); + } + return $value; + } + +} diff --git a/app/Cake/Controller/Component/PaginatorComponent.php b/app/Cake/Controller/Component/PaginatorComponent.php new file mode 100644 index 00000000..0cf29454 --- /dev/null +++ b/app/Cake/Controller/Component/PaginatorComponent.php @@ -0,0 +1,378 @@ +Paginator->settings = array( + * 'limit' => 20, + * 'maxLimit' => 100 + * ); + * }}} + * + * The above settings will be used to paginate any model. You can configure model specific settings by + * keying the settings with the model name. + * + * {{{ + * $this->Paginator->settings = array( + * 'Post' => array( + * 'limit' => 20, + * 'maxLimit' => 100 + * ), + * 'Comment' => array( ... ) + * ); + * }}} + * + * This would allow you to have different pagination settings for `Comment` and `Post` models. + * + * @package Cake.Controller.Component + */ +class PaginatorComponent extends Component { + +/** + * Pagination settings. These settings control pagination at a general level. + * You can also define sub arrays for pagination settings for specific models. + * + * - `maxLimit` The maximum limit users can choose to view. Defaults to 100 + * - `limit` The initial number of items per page. Defaults to 20. + * - `page` The starting page, defaults to 1. + * - `paramType` What type of parameters you want pagination to use? + * - `named` Use named parameters / routed parameters. + * - `querystring` Use query string parameters. + * + * @var array + */ + public $settings = array( + 'page' => 1, + 'limit' => 20, + 'maxLimit' => 100, + 'paramType' => 'named' + ); + +/** + * A list of parameters users are allowed to set using request parameters. Modifying + * this list will allow users to have more influence over pagination, + * be careful with what you permit. + * + * @var array + */ + public $whitelist = array( + 'limit', 'sort', 'page', 'direction' + ); + +/** + * Constructor + * + * @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components + * @param array $settings Array of configuration settings. + */ + public function __construct(ComponentCollection $collection, $settings = array()) { + $settings = array_merge($this->settings, (array)$settings); + $this->Controller = $collection->getController(); + parent::__construct($collection, $settings); + } + +/** + * Handles automatic pagination of model records. + * + * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel') + * @param mixed $scope Additional find conditions to use while paginating + * @param array $whitelist List of allowed fields for ordering. This allows you to prevent ordering + * on non-indexed, or undesirable columns. + * @return array Model query results + * @throws MissingModelException + */ + public function paginate($object = null, $scope = array(), $whitelist = array()) { + if (is_array($object)) { + $whitelist = $scope; + $scope = $object; + $object = null; + } + + $object = $this->_getObject($object); + + if (!is_object($object)) { + throw new MissingModelException($object); + } + + $options = $this->mergeOptions($object->alias); + $options = $this->validateSort($object, $options, $whitelist); + $options = $this->checkLimit($options); + + $conditions = $fields = $order = $limit = $page = $recursive = null; + + if (!isset($options['conditions'])) { + $options['conditions'] = array(); + } + + $type = 'all'; + + if (isset($options[0])) { + $type = $options[0]; + unset($options[0]); + } + + extract($options); + + if (is_array($scope) && !empty($scope)) { + $conditions = array_merge($conditions, $scope); + } elseif (is_string($scope)) { + $conditions = array($conditions, $scope); + } + if ($recursive === null) { + $recursive = $object->recursive; + } + + $extra = array_diff_key($options, compact( + 'conditions', 'fields', 'order', 'limit', 'page', 'recursive' + )); + if ($type !== 'all') { + $extra['type'] = $type; + } + + if (intval($page) < 1) { + $page = 1; + } + $page = $options['page'] = (int)$page; + + if ($object->hasMethod('paginate')) { + $results = $object->paginate( + $conditions, $fields, $order, $limit, $page, $recursive, $extra + ); + } else { + $parameters = compact('conditions', 'fields', 'order', 'limit', 'page'); + if ($recursive != $object->recursive) { + $parameters['recursive'] = $recursive; + } + $results = $object->find($type, array_merge($parameters, $extra)); + } + $defaults = $this->getDefaults($object->alias); + unset($defaults[0]); + + if ($object->hasMethod('paginateCount')) { + $count = $object->paginateCount($conditions, $recursive, $extra); + } else { + $parameters = compact('conditions'); + if ($recursive != $object->recursive) { + $parameters['recursive'] = $recursive; + } + $count = $object->find('count', array_merge($parameters, $extra)); + } + $pageCount = intval(ceil($count / $limit)); + + $paging = array( + 'page' => $page, + 'current' => count($results), + 'count' => $count, + 'prevPage' => ($page > 1), + 'nextPage' => ($count > ($page * $limit)), + 'pageCount' => $pageCount, + 'order' => $order, + 'limit' => $limit, + 'options' => Set::diff($options, $defaults), + 'paramType' => $options['paramType'] + ); + if (!isset($this->Controller->request['paging'])) { + $this->Controller->request['paging'] = array(); + } + $this->Controller->request['paging'] = array_merge( + (array)$this->Controller->request['paging'], + array($object->alias => $paging) + ); + + if ( + !in_array('Paginator', $this->Controller->helpers) && + !array_key_exists('Paginator', $this->Controller->helpers) + ) { + $this->Controller->helpers[] = 'Paginator'; + } + return $results; + } + +/** + * Get the object pagination will occur on. + * + * @param mixed $object The object you are looking for. + * @return mixed The model object to paginate on. + */ + protected function _getObject($object) { + if (is_string($object)) { + $assoc = null; + if (strpos($object, '.') !== false) { + list($object, $assoc) = pluginSplit($object); + } + + if ($assoc && isset($this->Controller->{$object}->{$assoc})) { + $object = $this->Controller->{$object}->{$assoc}; + } elseif ( + $assoc && isset($this->Controller->{$this->Controller->modelClass}) && + isset($this->Controller->{$this->Controller->modelClass}->{$assoc} + )) { + $object = $this->Controller->{$this->Controller->modelClass}->{$assoc}; + } elseif (isset($this->Controller->{$object})) { + $object = $this->Controller->{$object}; + } elseif ( + isset($this->Controller->{$this->Controller->modelClass}) && isset($this->Controller->{$this->Controller->modelClass}->{$object} + )) { + $object = $this->Controller->{$this->Controller->modelClass}->{$object}; + } + } elseif (empty($object) || $object === null) { + if (isset($this->Controller->{$this->Controller->modelClass})) { + $object = $this->Controller->{$this->Controller->modelClass}; + } else { + $className = null; + $name = $this->Controller->uses[0]; + if (strpos($this->Controller->uses[0], '.') !== false) { + list($name, $className) = explode('.', $this->Controller->uses[0]); + } + if ($className) { + $object = $this->Controller->{$className}; + } else { + $object = $this->Controller->{$name}; + } + } + } + return $object; + } + +/** + * Merges the various options that Pagination uses. + * Pulls settings together from the following places: + * + * - General pagination settings + * - Model specific settings. + * - Request parameters + * + * The result of this method is the aggregate of all the option sets combined together. You can change + * PaginatorComponent::$whitelist to modify which options/values can be set using request parameters. + * + * @param string $alias Model alias being paginated, if the general settings has a key with this value + * that key's settings will be used for pagination instead of the general ones. + * @return array Array of merged options. + */ + public function mergeOptions($alias) { + $defaults = $this->getDefaults($alias); + switch ($defaults['paramType']) { + case 'named': + $request = $this->Controller->request->params['named']; + break; + case 'querystring': + $request = $this->Controller->request->query; + break; + } + $request = array_intersect_key($request, array_flip($this->whitelist)); + return array_merge($defaults, $request); + } + +/** + * Get the default settings for a $model. If there are no settings for a specific model, the general settings + * will be used. + * + * @param string $alias Model name to get default settings for. + * @return array An array of pagination defaults for a model, or the general settings. + */ + public function getDefaults($alias) { + if (isset($this->settings[$alias])) { + $defaults = $this->settings[$alias]; + } else { + $defaults = $this->settings; + } + return array_merge( + array('page' => 1, 'limit' => 20, 'maxLimit' => 100, 'paramType' => 'named'), + $defaults + ); + } + +/** + * Validate that the desired sorting can be performed on the $object. Only fields or + * virtualFields can be sorted on. The direction param will also be sanitized. Lastly + * sort + direction keys will be converted into the model friendly order key. + * + * You can use the whitelist parameter to control which columns/fields are available for sorting. + * This helps prevent users from ordering large result sets on un-indexed values. + * + * @param Model $object The model being paginated. + * @param array $options The pagination options being used for this request. + * @param array $whitelist The list of columns that can be used for sorting. If empty all keys are allowed. + * @return array An array of options with sort + direction removed and replaced with order if possible. + */ + public function validateSort($object, $options, $whitelist = array()) { + if (isset($options['sort'])) { + $direction = null; + if (isset($options['direction'])) { + $direction = strtolower($options['direction']); + } + if ($direction != 'asc' && $direction != 'desc') { + $direction = 'asc'; + } + $options['order'] = array($options['sort'] => $direction); + } + + if (!empty($whitelist)) { + $field = key($options['order']); + if (!in_array($field, $whitelist)) { + $options['order'] = null; + } + } + + if (!empty($options['order']) && is_array($options['order'])) { + $alias = $object->alias ; + $key = $field = key($options['order']); + + if (strpos($key, '.') !== false) { + list($alias, $field) = explode('.', $key); + } + $value = $options['order'][$key]; + unset($options['order'][$key]); + + if ($object->hasField($field)) { + $options['order'][$alias . '.' . $field] = $value; + } elseif ($object->hasField($field, true)) { + $options['order'][$field] = $value; + } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field)) { + $options['order'][$alias . '.' . $field] = $value; + } + } + + return $options; + } + +/** + * Check the limit parameter and ensure its within the maxLimit bounds. + * + * @param array $options An array of options with a limit key to be checked. + * @return array An array of options for pagination + */ + public function checkLimit($options) { + $options['limit'] = (int) $options['limit']; + if (empty($options['limit']) || $options['limit'] < 1) { + $options['limit'] = 1; + } + $options['limit'] = min((int)$options['limit'], $options['maxLimit']); + return $options; + } +} \ No newline at end of file diff --git a/app/Cake/Controller/Component/RequestHandlerComponent.php b/app/Cake/Controller/Component/RequestHandlerComponent.php new file mode 100644 index 00000000..b76963d5 --- /dev/null +++ b/app/Cake/Controller/Component/RequestHandlerComponent.php @@ -0,0 +1,674 @@ + array('json_decode', true) + ); + +/** + * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT + * + * @param ComponentCollection $collection ComponentCollection object. + * @param array $settings Array of settings. + */ + public function __construct(ComponentCollection $collection, $settings = array()) { + $this->addInputType('xml', array(array($this, 'convertXml'))); + parent::__construct($collection, $settings); + } + +/** + * Initializes the component, gets a reference to Controller::$parameters, and + * checks to see if a file extension has been parsed by the Router. Or if the + * HTTP_ACCEPT_TYPE is set to a single value that is a supported extension and mapped type. + * If yes, RequestHandler::$ext is set to that value + * + * @param Controller $controller A reference to the controller + * @param array $settings Array of settings to _set(). + * @return void + * @see Router::parseExtensions() + */ + public function initialize($controller, $settings = array()) { + $this->request = $controller->request; + $this->response = $controller->response; + if (isset($this->request->params['ext'])) { + $this->ext = $this->request->params['ext']; + } + if (empty($this->ext) || $this->ext == 'html') { + $accepts = $this->request->accepts(); + $extensions = Router::extensions(); + if (count($accepts) == 1) { + $mapped = $this->mapType($accepts[0]); + if (in_array($mapped, $extensions)) { + $this->ext = $mapped; + } + } + } + $this->params = $controller->params; + $this->_set($settings); + } + +/** + * The startup method of the RequestHandler enables several automatic behaviors + * related to the detection of certain properties of the HTTP request, including: + * + * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header) + * - If Router::parseExtensions() is enabled, the layout and template type are + * switched based on the parsed extension or Accept-Type header. For example, if `controller/action.xml` + * is requested, the view path becomes `app/View/Controller/xml/action.ctp`. Also if + * `controller/action` is requested with `Accept-Type: application/xml` in the headers + * the view path will become `app/View/Controller/xml/action.ctp`. + * - If a helper with the same name as the extension exists, it is added to the controller. + * - If the extension is of a type that RequestHandler understands, it will set that + * Content-type in the response header. + * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned + * to the $data property of the controller, which can then be saved to a model object. + * + * @param Controller $controller A reference to the controller + * @return void + */ + public function startup($controller) { + $controller->request->params['isAjax'] = $this->request->is('ajax'); + $isRecognized = ( + !in_array($this->ext, array('html', 'htm')) && + $this->response->getMimeType($this->ext) + ); + + if (!empty($this->ext) && $isRecognized) { + $this->renderAs($controller, $this->ext); + } elseif ($this->request->is('ajax')) { + $this->renderAs($controller, 'ajax'); + } elseif (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) { + $this->respondAs('html', array('charset' => Configure::read('App.encoding'))); + } + + foreach ($this->_inputTypeMap as $type => $handler) { + if ($this->requestedWith($type)) { + $input = call_user_func_array(array($controller->request, 'input'), $handler); + $controller->request->data = $input; + } + } + } + +/** + * Helper method to parse xml input data, due to lack of anonymous functions + * this lives here. + * + * @param string $xml + * @return array Xml array data + */ + public function convertXml($xml) { + try { + $xml = Xml::build($xml); + if (isset($xml->data)) { + return Xml::toArray($xml->data); + } + return Xml::toArray($xml); + } catch (XmlException $e) { + return array(); + } + } + +/** + * Handles (fakes) redirects for Ajax requests using requestAction() + * + * @param Controller $controller A reference to the controller + * @param string|array $url A string or array containing the redirect location + * @param mixed $status HTTP Status for redirect + * @param boolean $exit + * @return void + */ + public function beforeRedirect($controller, $url, $status = null, $exit = true) { + if (!$this->request->is('ajax')) { + return; + } + foreach ($_POST as $key => $val) { + unset($_POST[$key]); + } + if (is_array($url)) { + $url = Router::url($url + array('base' => false)); + } + if (!empty($status)) { + $statusCode = $this->response->httpCodes($status); + $code = key($statusCode); + $this->response->statusCode($code); + } + $this->response->body($this->requestAction($url, array('return', 'bare' => false))); + $this->response->send(); + $this->_stop(); + } + +/** + * Returns true if the current HTTP request is Ajax, false otherwise + * + * @return boolean True if call is Ajax + * @deprecated use `$this->request->is('ajax')` instead. + */ + public function isAjax() { + return $this->request->is('ajax'); + } + +/** + * Returns true if the current HTTP request is coming from a Flash-based client + * + * @return boolean True if call is from Flash + * @deprecated use `$this->request->is('flash')` instead. + */ + public function isFlash() { + return $this->request->is('flash'); + } + +/** + * Returns true if the current request is over HTTPS, false otherwise. + * + * @return boolean True if call is over HTTPS + * @deprecated use `$this->request->is('ssl')` instead. + */ + public function isSSL() { + return $this->request->is('ssl'); + } + +/** + * Returns true if the current call accepts an XML response, false otherwise + * + * @return boolean True if client accepts an XML response + */ + public function isXml() { + return $this->prefers('xml'); + } + +/** + * Returns true if the current call accepts an RSS response, false otherwise + * + * @return boolean True if client accepts an RSS response + */ + public function isRss() { + return $this->prefers('rss'); + } + +/** + * Returns true if the current call accepts an Atom response, false otherwise + * + * @return boolean True if client accepts an RSS response + */ + public function isAtom() { + return $this->prefers('atom'); + } + +/** + * Returns true if user agent string matches a mobile web browser, or if the + * client accepts WAP content. + * + * @return boolean True if user agent is a mobile web browser + */ + public function isMobile() { + return $this->request->is('mobile') || $this->accepts('wap'); + } + +/** + * Returns true if the client accepts WAP content + * + * @return boolean + */ + public function isWap() { + return $this->prefers('wap'); + } + +/** + * Returns true if the current call a POST request + * + * @return boolean True if call is a POST + * @deprecated Use $this->request->is('post'); from your controller. + */ + public function isPost() { + return $this->request->is('post'); + } + +/** + * Returns true if the current call a PUT request + * + * @return boolean True if call is a PUT + * @deprecated Use $this->request->is('put'); from your controller. + */ + public function isPut() { + return $this->request->is('put'); + } + +/** + * Returns true if the current call a GET request + * + * @return boolean True if call is a GET + * @deprecated Use $this->request->is('get'); from your controller. + */ + public function isGet() { + return $this->request->is('get'); + } + +/** + * Returns true if the current call a DELETE request + * + * @return boolean True if call is a DELETE + * @deprecated Use $this->request->is('delete'); from your controller. + */ + public function isDelete() { + return $this->request->is('delete'); + } + +/** + * Gets Prototype version if call is Ajax, otherwise empty string. + * The Prototype library sets a special "Prototype version" HTTP header. + * + * @return string Prototype version of component making Ajax call + */ + public function getAjaxVersion() { + if (env('HTTP_X_PROTOTYPE_VERSION') != null) { + return env('HTTP_X_PROTOTYPE_VERSION'); + } + return false; + } + +/** + * Adds/sets the Content-type(s) for the given name. This method allows + * content-types to be mapped to friendly aliases (or extensions), which allows + * RequestHandler to automatically respond to requests of that type in the + * startup method. + * + * @param string $name The name of the Content-type, i.e. "html", "xml", "css" + * @param mixed $type The Content-type or array of Content-types assigned to the name, + * i.e. "text/html", or "application/xml" + * @return void + * @deprecated use `$this->response->type()` instead. + */ + public function setContent($name, $type = null) { + $this->response->type(array($name => $type)); + } + +/** + * Gets the server name from which this request was referred + * + * @return string Server address + * @deprecated use $this->request->referer() from your controller instead + */ + public function getReferer() { + return $this->request->referer(false); + } + +/** + * Gets remote client IP + * + * @param boolean $safe + * @return string Client IP address + * @deprecated use $this->request->clientIp() from your, controller instead. + */ + public function getClientIP($safe = true) { + return $this->request->clientIp($safe); + } + +/** + * Determines which content types the client accepts. Acceptance is based on + * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT + * header. Unlike CakeRequest::accepts() this method deals entirely with mapped content types. + * + * Usage: + * + * `$this->RequestHandler->accepts(array('xml', 'html', 'json'));` + * + * Returns true if the client accepts any of the supplied types. + * + * `$this->RequestHandler->accepts('xml');` + * + * Returns true if the client accepts xml. + * + * @param mixed $type Can be null (or no parameter), a string type name, or an + * array of types + * @return mixed If null or no parameter is passed, returns an array of content + * types the client accepts. If a string is passed, returns true + * if the client accepts it. If an array is passed, returns true + * if the client accepts one or more elements in the array. + * @see RequestHandlerComponent::setContent() + */ + public function accepts($type = null) { + $accepted = $this->request->accepts(); + + if ($type == null) { + return $this->mapType($accepted); + } elseif (is_array($type)) { + foreach ($type as $t) { + $t = $this->mapAlias($t); + if (in_array($t, $accepted)) { + return true; + } + } + return false; + } elseif (is_string($type)) { + $type = $this->mapAlias($type); + return in_array($type, $accepted); + } + return false; + } + +/** + * Determines the content type of the data the client has sent (i.e. in a POST request) + * + * @param mixed $type Can be null (or no parameter), a string type name, or an array of types + * @return mixed If a single type is supplied a boolean will be returned. If no type is provided + * The mapped value of CONTENT_TYPE will be returned. If an array is supplied the first type + * in the request content type will be returned. + */ + public function requestedWith($type = null) { + if (!$this->request->is('post') && !$this->request->is('put')) { + return null; + } + + list($contentType) = explode(';', env('CONTENT_TYPE')); + if ($type == null) { + return $this->mapType($contentType); + } elseif (is_array($type)) { + foreach ($type as $t) { + if ($this->requestedWith($t)) { + return $t; + } + } + return false; + } elseif (is_string($type)) { + return ($type == $this->mapType($contentType)); + } + } + +/** + * Determines which content-types the client prefers. If no parameters are given, + * the content-type that the client most likely prefers is returned. If $type is + * an array, the first item in the array that the client accepts is returned. + * Preference is determined primarily by the file extension parsed by the Router + * if provided, and secondarily by the list of content-types provided in + * HTTP_ACCEPT. + * + * @param mixed $type An optional array of 'friendly' content-type names, i.e. + * 'html', 'xml', 'js', etc. + * @return mixed If $type is null or not provided, the first content-type in the + * list, based on preference, is returned. + * @see RequestHandlerComponent::setContent() + */ + public function prefers($type = null) { + $acceptRaw = $this->request->parseAccept(); + + if (empty($acceptRaw)) { + return $this->ext; + } + $accepts = array_shift($acceptRaw); + $accepts = $this->mapType($accepts); + + if ($type == null) { + if (empty($this->ext) && !empty($accepts)) { + return $accepts[0]; + } + return $this->ext; + } + + $types = (array)$type; + + if (count($types) === 1) { + if (!empty($this->ext)) { + return in_array($this->ext, $types); + } + return in_array($types[0], $accepts); + } + + $intersect = array_values(array_intersect($accepts, $types)); + if (empty($intersect)) { + return false; + } + return $intersect[0]; + } + +/** + * Sets the layout and template paths for the content type defined by $type. + * + * ### Usage: + * + * Render the response as an 'ajax' response. + * + * `$this->RequestHandler->renderAs($this, 'ajax');` + * + * Render the response as an xml file and force the result as a file download. + * + * `$this->RequestHandler->renderAs($this, 'xml', array('attachment' => 'myfile.xml');` + * + * @param Controller $controller A reference to a controller object + * @param string $type Type of response to send (e.g: 'ajax') + * @param array $options Array of options to use + * @return void + * @see RequestHandlerComponent::setContent() + * @see RequestHandlerComponent::respondAs() + */ + public function renderAs($controller, $type, $options = array()) { + $defaults = array('charset' => 'UTF-8'); + + if (Configure::read('App.encoding') !== null) { + $defaults['charset'] = Configure::read('App.encoding'); + } + $options = array_merge($defaults, $options); + + if ($type == 'ajax') { + $controller->layout = $this->ajaxLayout; + return $this->respondAs('html', $options); + } + $controller->ext = '.ctp'; + + if (empty($this->_renderType)) { + $controller->viewPath .= DS . $type; + } else { + $remove = preg_replace("/([\/\\\\]{$this->_renderType})$/", DS . $type, $controller->viewPath); + $controller->viewPath = $remove; + } + $this->_renderType = $type; + $controller->layoutPath = $type; + + if ($this->response->getMimeType($type)) { + $this->respondAs($type, $options); + } + + $helper = ucfirst($type); + $isAdded = ( + in_array($helper, $controller->helpers) || + array_key_exists($helper, $controller->helpers) + ); + + if (!$isAdded) { + App::uses($helper . 'Helper', 'View/Helper'); + if (class_exists($helper . 'Helper')) { + $controller->helpers[] = $helper; + } + } + } + +/** + * Sets the response header based on type map index name. This wraps several methods + * available on CakeResponse. It also allows you to use Content-Type aliases. + * + * @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type, + * like 'application/x-shockwave'. + * @param array $options If $type is a friendly type name that is associated with + * more than one type of content, $index is used to select which content-type to use. + * @return boolean Returns false if the friendly type name given in $type does + * not exist in the type map, or if the Content-type header has + * already been set by this method. + * @see RequestHandlerComponent::setContent() + */ + public function respondAs($type, $options = array()) { + $defaults = array('index' => null, 'charset' => null, 'attachment' => false); + $options = $options + $defaults; + + if (strpos($type, '/') === false) { + $cType = $this->response->getMimeType($type); + if ($cType === false) { + return false; + } + if (is_array($cType) && isset($cType[$options['index']])) { + $cType = $cType[$options['index']]; + } + if (is_array($cType)) { + if ($this->prefers($cType)) { + $cType = $this->prefers($cType); + } else { + $cType = $cType[0]; + } + } + } else { + $cType = $type; + } + + if ($cType != null) { + if (empty($this->request->params['requested'])) { + $this->response->type($cType); + } + + if (!empty($options['charset'])) { + $this->response->charset($options['charset']); + } + if (!empty($options['attachment'])) { + $this->response->download($options['attachment']); + } + return true; + } + return false; + } + +/** + * Returns the current response type (Content-type header), or null if not alias exists + * + * @return mixed A string content type alias, or raw content type if no alias map exists, + * otherwise null + */ + public function responseType() { + return $this->mapType($this->response->type()); + } + +/** + * Maps a content-type back to an alias + * + * @param mixed $cType Either a string content type to map, or an array of types. + * @return mixed Aliases for the types provided. + * @deprecated Use $this->response->mapType() in your controller instead. + */ + public function mapType($cType) { + return $this->response->mapType($cType); + } + +/** + * Maps a content type alias back to its mime-type(s) + * + * @param mixed $alias String alias to convert back into a content type. Or an array of aliases to map. + * @return mixed Null on an undefined alias. String value of the mapped alias type. If an + * alias maps to more than one content type, the first one will be returned. + */ + public function mapAlias($alias) { + if (is_array($alias)) { + return array_map(array($this, 'mapAlias'), $alias); + } + $type = $this->response->getMimeType($alias); + if ($type) { + if (is_array($type)) { + return $type[0]; + } + return $type; + } + return null; + } + +/** + * Add a new mapped input type. Mapped input types are automatically + * converted by RequestHandlerComponent during the startup() callback. + * + * @param string $type The type alias being converted, ie. json + * @param array $handler The handler array for the type. The first index should + * be the handling callback, all other arguments should be additional parameters + * for the handler. + * @return void + * @throws CakeException + */ + public function addInputType($type, $handler) { + if (!is_array($handler) || !isset($handler[0]) || !is_callable($handler[0])) { + throw new CakeException(__d('cake_dev', 'You must give a handler callback.')); + } + $this->_inputTypeMap[$type] = $handler; + } +} diff --git a/app/Cake/Controller/Component/SecurityComponent.php b/app/Cake/Controller/Component/SecurityComponent.php new file mode 100644 index 00000000..0bfea178 --- /dev/null +++ b/app/Cake/Controller/Component/SecurityComponent.php @@ -0,0 +1,569 @@ +request = $controller->request; + $this->_action = $this->request->params['action']; + $this->_methodsRequired($controller); + $this->_secureRequired($controller); + $this->_authRequired($controller); + + $isPost = ($this->request->is('post') || $this->request->is('put')); + $isNotRequestAction = ( + !isset($controller->request->params['requested']) || + $controller->request->params['requested'] != 1 + ); + + if ($isPost && $isNotRequestAction && $this->validatePost) { + if ($this->_validatePost($controller) === false) { + return $this->blackHole($controller, 'auth'); + } + } + if ($isPost && $isNotRequestAction && $this->csrfCheck) { + if ($this->_validateCsrf($controller) === false) { + return $this->blackHole($controller, 'csrf'); + } + } + $this->_generateToken($controller); + } + +/** + * Sets the actions that require a POST request, or empty for all actions + * + * @return void + * @link http://book.cakephp.org/view/1299/requirePost + */ + public function requirePost() { + $args = func_get_args(); + $this->_requireMethod('Post', $args); + } + +/** + * Sets the actions that require a GET request, or empty for all actions + * + * @return void + */ + public function requireGet() { + $args = func_get_args(); + $this->_requireMethod('Get', $args); + } + +/** + * Sets the actions that require a PUT request, or empty for all actions + * + * @return void + */ + public function requirePut() { + $args = func_get_args(); + $this->_requireMethod('Put', $args); + } + +/** + * Sets the actions that require a DELETE request, or empty for all actions + * + * @return void + */ + public function requireDelete() { + $args = func_get_args(); + $this->_requireMethod('Delete', $args); + } + +/** + * Sets the actions that require a request that is SSL-secured, or empty for all actions + * + * @return void + * @link http://book.cakephp.org/view/1300/requireSecure + */ + public function requireSecure() { + $args = func_get_args(); + $this->_requireMethod('Secure', $args); + } + +/** + * Sets the actions that require an authenticated request, or empty for all actions + * + * @return void + * @link http://book.cakephp.org/view/1301/requireAuth + */ + public function requireAuth() { + $args = func_get_args(); + $this->_requireMethod('Auth', $args); + } + +/** + * Black-hole an invalid request with a 404 error or custom callback. If SecurityComponent::$blackHoleCallback + * is specified, it will use this callback by executing the method indicated in $error + * + * @param Controller $controller Instantiating controller + * @param string $error Error method + * @return mixed If specified, controller blackHoleCallback's response, or no return otherwise + * @see SecurityComponent::$blackHoleCallback + * @link http://book.cakephp.org/view/1307/blackHole-object-controller-string-error + */ + public function blackHole($controller, $error = '') { + if ($this->blackHoleCallback == null) { + $code = 404; + if ($error == 'login') { + $code = 401; + $controller->header($this->loginRequest()); + } + return $controller->redirect(null, $code, true); + } else { + return $this->_callback($controller, $this->blackHoleCallback, array($error)); + } + } + +/** + * Sets the actions that require a $method HTTP request, or empty for all actions + * + * @param string $method The HTTP method to assign controller actions to + * @param array $actions Controller actions to set the required HTTP method to. + * @return void + */ + protected function _requireMethod($method, $actions = array()) { + if (isset($actions[0]) && is_array($actions[0])) { + $actions = $actions[0]; + } + $this->{'require' . $method} = (empty($actions)) ? array('*'): $actions; + } + +/** + * Check if HTTP methods are required + * + * @param Controller $controller Instantiating controller + * @return boolean true if $method is required + */ + protected function _methodsRequired($controller) { + foreach (array('Post', 'Get', 'Put', 'Delete') as $method) { + $property = 'require' . $method; + if (is_array($this->$property) && !empty($this->$property)) { + $require = $this->$property; + if (in_array($this->_action, $require) || $this->$property == array('*')) { + if (!$this->request->is($method)) { + if (!$this->blackHole($controller, $method)) { + return null; + } + } + } + } + } + return true; + } + +/** + * Check if access requires secure connection + * + * @param Controller $controller Instantiating controller + * @return boolean true if secure connection required + */ + protected function _secureRequired($controller) { + if (is_array($this->requireSecure) && !empty($this->requireSecure)) { + $requireSecure = $this->requireSecure; + + if (in_array($this->_action, $requireSecure) || $this->requireSecure == array('*')) { + if (!$this->request->is('ssl')) { + if (!$this->blackHole($controller, 'secure')) { + return null; + } + } + } + } + return true; + } + +/** + * Check if authentication is required + * + * @param Controller $controller Instantiating controller + * @return boolean true if authentication required + */ + protected function _authRequired($controller) { + if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($this->request->data)) { + $requireAuth = $this->requireAuth; + + if (in_array($this->request->params['action'], $requireAuth) || $this->requireAuth == array('*')) { + if (!isset($controller->request->data['_Token'] )) { + if (!$this->blackHole($controller, 'auth')) { + return null; + } + } + + if ($this->Session->check('_Token')) { + $tData = $this->Session->read('_Token'); + + if (!empty($tData['allowedControllers']) && !in_array($this->request->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($this->request->params['action'], $tData['allowedActions'])) { + if (!$this->blackHole($controller, 'auth')) { + return null; + } + } + } else { + if (!$this->blackHole($controller, 'auth')) { + return null; + } + } + } + } + return true; + } + +/** + * Validate submitted form + * + * @param Controller $controller Instantiating controller + * @return boolean true if submitted form is valid + */ + protected function _validatePost($controller) { + if (empty($controller->request->data)) { + return true; + } + $data = $controller->request->data; + + if (!isset($data['_Token']) || !isset($data['_Token']['fields']) || !isset($data['_Token']['unlocked'])) { + return false; + } + + $locked = ''; + $check = $controller->request->data; + $token = urldecode($check['_Token']['fields']); + $unlocked = urldecode($check['_Token']['unlocked']); + + if (strpos($token, ':')) { + list($token, $locked) = explode(':', $token, 2); + } + unset($check['_Token']); + + $locked = explode('|', $locked); + $unlocked = explode('|', $unlocked); + + $lockedFields = array(); + $fields = Set::flatten($check); + $fieldList = array_keys($fields); + $multi = array(); + + foreach ($fieldList as $i => $key) { + if (preg_match('/\.\d+$/', $key)) { + $multi[$i] = preg_replace('/\.\d+$/', '', $key); + unset($fieldList[$i]); + } + } + if (!empty($multi)) { + $fieldList += array_unique($multi); + } + + $unlockedFields = array_unique( + array_merge((array)$this->disabledFields, (array)$this->unlockedFields, $unlocked) + ); + + foreach ($fieldList as $i => $key) { + $isDisabled = false; + $isLocked = (is_array($locked) && in_array($key, $locked)); + + if (!empty($unlockedFields)) { + foreach ($unlockedFields as $off) { + $off = explode('.', $off); + $field = array_values(array_intersect(explode('.', $key), $off)); + $isUnlocked = ($field === $off); + if ($isUnlocked) { + break; + } + } + } + + if ($isUnlocked || $isLocked) { + unset($fieldList[$i]); + if ($isLocked) { + $lockedFields[$key] = $fields[$key]; + } + } + } + sort($unlocked, SORT_STRING); + sort($fieldList, SORT_STRING); + ksort($lockedFields, SORT_STRING); + + $fieldList += $lockedFields; + $unlocked = implode('|', $unlocked); + $check = Security::hash(serialize($fieldList) . $unlocked . Configure::read('Security.salt')); + return ($token === $check); + } + +/** + * Add authentication key for new form posts + * + * @param Controller $controller Instantiating controller + * @return boolean Success + */ + protected function _generateToken($controller) { + if (isset($controller->request->params['requested']) && $controller->request->params['requested'] === 1) { + if ($this->Session->check('_Token')) { + $tokenData = $this->Session->read('_Token'); + $controller->request->params['_Token'] = $tokenData; + } + return false; + } + $authKey = Security::generateAuthKey(); + $token = array( + 'key' => $authKey, + 'allowedControllers' => $this->allowedControllers, + 'allowedActions' => $this->allowedActions, + 'unlockedFields' => array_merge($this->disabledFields, $this->unlockedFields), + 'csrfTokens' => array() + ); + + $tokenData = array(); + if ($this->Session->check('_Token')) { + $tokenData = $this->Session->read('_Token'); + if (!empty($tokenData['csrfTokens']) && is_array($tokenData['csrfTokens'])) { + $token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']); + } + } + if ($this->csrfCheck && ($this->csrfUseOnce || empty($token['csrfTokens'])) ) { + $token['csrfTokens'][$authKey] = strtotime($this->csrfExpires); + } + if ($this->csrfCheck && $this->csrfUseOnce == false) { + $csrfTokens = array_keys($token['csrfTokens']); + $token['key'] = $csrfTokens[0]; + } + $this->Session->write('_Token', $token); + $controller->request->params['_Token'] = array( + 'key' => $token['key'], + 'unlockedFields' => $token['unlockedFields'] + ); + return true; + } + +/** + * Validate that the controller has a CSRF token in the POST data + * and that the token is legit/not expired. If the token is valid + * it will be removed from the list of valid tokens. + * + * @param Controller $controller A controller to check + * @return boolean Valid csrf token. + */ + protected function _validateCsrf($controller) { + $token = $this->Session->read('_Token'); + $requestToken = $controller->request->data('_Token.key'); + if (isset($token['csrfTokens'][$requestToken]) && $token['csrfTokens'][$requestToken] >= time()) { + if ($this->csrfUseOnce) { + $this->Session->delete('_Token.csrfTokens.' . $requestToken); + } + return true; + } + return false; + } + +/** + * Expire CSRF nonces and remove them from the valid tokens. + * Uses a simple timeout to expire the tokens. + * + * @param array $tokens An array of nonce => expires. + * @return array An array of nonce => expires. + */ + protected function _expireTokens($tokens) { + $now = time(); + foreach ($tokens as $nonce => $expires) { + if ($expires < $now) { + unset($tokens[$nonce]); + } + } + return $tokens; + } + +/** + * Calls a controller callback method + * + * @param Controller $controller Controller to run callback on + * @param string $method Method to execute + * @param array $params Parameters to send to method + * @return mixed Controller callback method's response + */ + protected function _callback($controller, $method, $params = array()) { + if (is_callable(array($controller, $method))) { + return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params); + } else { + return null; + } + } +} diff --git a/app/Cake/Controller/Component/SessionComponent.php b/app/Cake/Controller/Component/SessionComponent.php new file mode 100644 index 00000000..f543427b --- /dev/null +++ b/app/Cake/Controller/Component/SessionComponent.php @@ -0,0 +1,187 @@ +Session->write('Controller.sessKey', 'session value'); + * + * @param string $name The name of the key your are setting in the session. + * This should be in a Controller.key format for better organizing + * @param string $value The value you want to store in a session. + * @return boolean Success + * @link http://book.cakephp.org/view/1312/write + */ + public function write($name, $value = null) { + return CakeSession::write($name, $value); + } + +/** + * Used to read a session values for a key or return values for all keys. + * + * In your controller: $this->Session->read('Controller.sessKey'); + * Calling the method without a param will return all session vars + * + * @param string $name the name of the session key you want to read + * @return mixed value from the session vars + * @link http://book.cakephp.org/view/1314/read + */ + public function read($name = null) { + return CakeSession::read($name); + } + +/** + * Wrapper for SessionComponent::del(); + * + * In your controller: $this->Session->delete('Controller.sessKey'); + * + * @param string $name the name of the session key you want to delete + * @return boolean true is session variable is set and can be deleted, false is variable was not set. + * @link http://book.cakephp.org/view/1316/delete + */ + public function delete($name) { + return CakeSession::delete($name); + } + +/** + * Used to check if a session variable is set + * + * In your controller: $this->Session->check('Controller.sessKey'); + * + * @param string $name the name of the session key you want to check + * @return boolean true is session variable is set, false if not + * @link http://book.cakephp.org/view/1315/check + */ + public function check($name) { + return CakeSession::check($name); + } + +/** + * Used to determine the last error in a session. + * + * In your controller: $this->Session->error(); + * + * @return string Last session error + * @link http://book.cakephp.org/view/1318/error + */ + public function error() { + return CakeSession::error(); + } + +/** + * Used to set a session variable that can be used to output messages in the view. + * + * In your controller: $this->Session->setFlash('This has been saved'); + * + * Additional params below can be passed to customize the output, or the Message.[key]. + * You can also set additional parameters when rendering flash messages. See SessionHelper::flash() + * for more information on how to do that. + * + * @param string $message Message to be flashed + * @param string $element Element to wrap flash message in. + * @param array $params Parameters to be sent to layout as view variables + * @param string $key Message key, default is 'flash' + * @return void + * @link http://book.cakephp.org/view/1313/setFlash + */ + public function setFlash($message, $element = 'default', $params = array(), $key = 'flash') { + CakeSession::write('Message.' . $key, compact('message', 'element', 'params')); + } + +/** + * Used to renew a session id + * + * In your controller: $this->Session->renew(); + * + * @return void + */ + public function renew() { + return CakeSession::renew(); + } + +/** + * Used to check for a valid session. + * + * In your controller: $this->Session->valid(); + * + * @return boolean true is session is valid, false is session is invalid + */ + public function valid() { + return CakeSession::valid(); + } + +/** + * Used to destroy sessions + * + * In your controller: $this->Session->destroy(); + * + * @return void + * @link http://book.cakephp.org/view/1317/destroy + */ + public function destroy() { + return CakeSession::destroy(); + } + +/** + * Returns Session id + * + * If $id is passed in a beforeFilter, the Session will be started + * with the specified id + * + * @param string $id + * @return string + */ + public function id($id = null) { + return CakeSession::id($id); + } + +/** + * Returns a bool, whether or not the session has been started. + * + * @return boolean + */ + public function started() { + return CakeSession::started(); + } + +} diff --git a/app/Cake/Controller/ComponentCollection.php b/app/Cake/Controller/ComponentCollection.php new file mode 100644 index 00000000..68cc443b --- /dev/null +++ b/app/Cake/Controller/ComponentCollection.php @@ -0,0 +1,106 @@ +components)) { + return; + } + $this->_Controller = $Controller; + $components = ComponentCollection::normalizeObjectArray($Controller->components); + foreach ($components as $name => $properties) { + $Controller->{$name} = $this->load($properties['class'], $properties['settings']); + } + } + +/** + * Get the controller associated with the collection. + * + * @return Controller. + */ + public function getController() { + return $this->_Controller; + } + +/** + * Loads/constructs a component. Will return the instance in the registry if it already exists. + * You can use `$settings['enabled'] = false` to disable callbacks on a component when loading it. + * Callbacks default to on. Disabled component methods work as normal, only callbacks are disabled. + * + * You can alias your component as an existing component by setting the 'className' key, i.e., + * {{{ + * public $components = array( + * 'Email' => array( + * 'className' => 'AliasedEmail' + * ); + * ); + * }}} + * All calls to the `Email` component would use `AliasedEmail` instead. + * + * @param string $component Component name to load + * @param array $settings Settings for the component. + * @return Component A component object, Either the existing loaded component or a new one. + * @throws MissingComponentFileException, MissingComponentClassException when the component could not be found + */ + public function load($component, $settings = array()) { + if (is_array($settings) && isset($settings['className'])) { + $alias = $component; + $component = $settings['className']; + } + list($plugin, $name) = pluginSplit($component, true); + if (!isset($alias)) { + $alias = $name; + } + if (isset($this->_loaded[$alias])) { + return $this->_loaded[$alias]; + } + $componentClass = $name . 'Component'; + App::uses($componentClass, $plugin . 'Controller/Component'); + if (!class_exists($componentClass)) { + throw new MissingComponentClassException(array( + 'file' => Inflector::underscore($componentClass) . '.php', + 'class' => $componentClass + )); + } + $this->_loaded[$alias] = new $componentClass($this, $settings); + $enable = isset($settings['enabled']) ? $settings['enabled'] : true; + if ($enable === true) { + $this->_enabled[] = $alias; + } + return $this->_loaded[$alias]; + } + +} \ No newline at end of file diff --git a/app/Cake/Controller/Controller.php b/app/Cake/Controller/Controller.php new file mode 100644 index 00000000..083d261c --- /dev/null +++ b/app/Cake/Controller/Controller.php @@ -0,0 +1,1169 @@ +request`. The request object contains all the POST, GET and FILES + * that were part of the request. + * + * After performing the required actions, controllers are responsible for creating a response. This usually + * takes the form of a generated View, or possibly a redirection to another controller action. In either case + * `$this->response` allows you to manipulate all aspects of the response. + * + * Controllers are created by Dispatcher based on request parameters and routing. By default controllers and actions + * use conventional names. For example `/posts/index` maps to `PostsController::index()`. You can re-map urls + * using Router::connect(). + * + * @package Cake.Controller + * @property AclComponent $Acl + * @property AuthComponent $Auth + * @property CookieComponent $Cookie + * @property EmailComponent $Email + * @property PaginatorComponent $Paginator + * @property RequestHandlerComponent $RequestHandler + * @property SecurityComponent $Security + * @property SessionComponent $Session + * @link http://book.cakephp.org/view/956/Introduction + */ +class Controller extends Object { + +/** + * The name of this controller. Controller names are plural, named after the model they manipulate. + * + * @var string + * @link http://book.cakephp.org/view/959/Controller-Attributes + */ + public $name = null; + +/** + * An array containing the class names of models this controller uses. + * + * Example: `public $uses = array('Product', 'Post', 'Comment');` + * + * Can be set to array() to use no models. Can be set to false to + * use no models and prevent the merging of $uses with AppController + * + * @var mixed A single name as a string or a list of names as an array. + * @link http://book.cakephp.org/view/961/components-helpers-and-uses + */ + public $uses = false; + +/** + * An array containing the names of helpers this controller uses. The array elements should + * not contain the "Helper" part of the classname. + * + * Example: `public $helpers = array('Html', 'Javascript', 'Time', 'Ajax');` + * + * @var mixed A single name as a string or a list of names as an array. + * @link http://book.cakephp.org/view/961/components-helpers-and-uses + */ + public $helpers = array('Session', 'Html', 'Form'); + +/** + * An instance of a CakeRequest object that contains information about the current request. + * This object contains all the information about a request and several methods for reading + * additional information about the request. + * + * @var CakeRequest + */ + public $request; + +/** + * An instance of a CakeResponse object that contains information about the impending response + * + * @var CakeResponse + */ + public $response; + +/** + * The classname to use for creating the response object. + * + * @var string + */ + protected $_responseClass = 'CakeResponse'; + +/** + * The name of the views subfolder containing views for this controller. + * + * @var string + */ + public $viewPath = null; + +/** + * The name of the layouts subfolder containing layouts for this controller. + * + * @var string + */ + public $layoutPath = null; + +/** + * Contains variables to be handed to the view. + * + * @var array + */ + public $viewVars = array(); + + +/** + * The name of the view file to render. The name specified + * is the filename in /app/View/ without the .ctp extension. + * + * @var string + * @link http://book.cakephp.org/view/962/Page-related-Attributes-layout-and-pageTitle + */ + public $view = null; + +/** + * The name of the layout file to render the view inside of. The name specified + * is the filename of the layout in /app/View/Layouts without the .ctp + * extension. + * + * @var string + * @link http://book.cakephp.org/view/962/Page-related-Attributes-layout-and-pageTitle + */ + public $layout = 'default'; + +/** + * Set to true to automatically render the view + * after action logic. + * + * @var boolean + */ + public $autoRender = true; + +/** + * Set to true to automatically render the layout around views. + * + * @var boolean + */ + public $autoLayout = true; + +/** + * Instance of ComponentCollection used to handle callbacks. + * + * @var string + */ + public $Components = null; + +/** + * Array containing the names of components this controller uses. Component names + * should not contain the "Component" portion of the classname. + * + * Example: `public $components = array('Session', 'RequestHandler', 'Acl');` + * + * @var array + * @link http://book.cakephp.org/view/961/components-helpers-and-uses + */ + public $components = array('Session'); + +/** + * The name of the View class this controller sends output to. + * + * @var string + */ + public $viewClass = 'View'; + +/** + * Instance of the View created during rendering. Won't be set until after Controller::render() is called. + * + * @var View + */ + public $View; + +/** + * File extension for view templates. Defaults to Cake's conventional ".ctp". + * + * @var string + */ + public $ext = '.ctp'; + +/** + * Automatically set to the name of a plugin. + * + * @var string + */ + public $plugin = null; + +/** + * Used to define methods a controller that will be cached. To cache a + * single action, the value is set to an array containing keys that match + * action names and values that denote cache expiration times (in seconds). + * + * Example: + * + * {{{ + * public $cacheAction = array( + * 'view/23/' => 21600, + * 'recalled/' => 86400 + * ); + * }}} + * + * $cacheAction can also be set to a strtotime() compatible string. This + * marks all the actions in the controller for view caching. + * + * @var mixed + * @link http://book.cakephp.org/view/1380/Caching-in-the-Controller + */ + public $cacheAction = false; + +/** + * Holds all params passed and named. + * + * @var mixed + */ + public $passedArgs = array(); + +/** + * Triggers Scaffolding + * + * @var mixed + * @link http://book.cakephp.org/view/1103/Scaffolding + */ + public $scaffold = false; + +/** + * Holds current methods of the controller. This is a list of all the methods reachable + * via url. Modifying this array, will allow you to change which methods can be reached. + * + * @var array + */ + public $methods = array(); + +/** + * This controller's primary model class name, the Inflector::classify()'ed version of + * the controller's $name property. + * + * Example: For a controller named 'Comments', the modelClass would be 'Comment' + * + * @var string + */ + public $modelClass = null; + +/** + * This controller's model key name, an underscored version of the controller's $modelClass property. + * + * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment' + * + * @var string + */ + public $modelKey = null; + +/** + * Holds any validation errors produced by the last call of the validateErrors() method/ + * + * @var array Validation errors, or false if none + */ + public $validationErrors = null; + +/** + * The class name of the parent class you wish to merge with. + * Typically this is AppController, but you may wish to merge vars with a different + * parent class. + * + * @var string + */ + protected $_mergeParent = 'AppController'; + +/** + * Constructor. + * + * @param CakeRequest $request Request object for this controller. Can be null for testing, + * but expect that features that use the request parameters will not work. + * @param CakeResponse $response Response object for this controller. + */ + public function __construct($request = null, $response = null) { + if ($this->name === null) { + $this->name = substr(get_class($this), 0, strlen(get_class($this)) -10); + } + + if ($this->viewPath == null) { + $this->viewPath = $this->name; + } + + $this->modelClass = Inflector::singularize($this->name); + $this->modelKey = Inflector::underscore($this->modelClass); + $this->Components = new ComponentCollection(); + + $childMethods = get_class_methods($this); + $parentMethods = get_class_methods('Controller'); + + $this->methods = array_diff($childMethods, $parentMethods); + + if ($request instanceof CakeRequest) { + $this->setRequest($request); + } + if ($response instanceof CakeResponse) { + $this->response = $response; + } + parent::__construct(); + } + +/** + * Provides backwards compatibility to avoid problems with empty and isset to alias properties. + * Lazy loads models using the loadModel() method if declared in $uses + * + * @param string $name + * @return void + */ + public function __isset($name) { + switch ($name) { + case 'base': + case 'here': + case 'webroot': + case 'data': + case 'action': + case 'params': + return true; + } + + if (is_array($this->uses)) { + foreach ($this->uses as $modelClass) { + list($plugin, $class) = pluginSplit($modelClass, true); + if ($name === $class) { + if (!$plugin) { + $plugin = $this->plugin ? $this->plugin . '.' : null; + } + return $this->loadModel($modelClass); + } + } + } + + if ($name === $this->modelClass) { + list($plugin, $class) = pluginSplit($name, true); + if (!$plugin) { + $plugin = $this->plugin ? $this->plugin . '.' : null; + } + return $this->loadModel($plugin . $this->modelClass); + } + + return false; + } + +/** + * Provides backwards compatibility access to the request object properties. + * Also provides the params alias. + * + * @param string $name + * @return void + */ + public function __get($name) { + switch ($name) { + case 'base': + case 'here': + case 'webroot': + case 'data': + return $this->request->{$name}; + case 'action': + return isset($this->request->params['action']) ? $this->request->params['action'] : ''; + case 'params': + return $this->request; + case 'paginate': + return $this->Components->load('Paginator')->settings; + } + + if (isset($this->{$name})) { + return $this->{$name}; + } + + return null; + } + +/** + * Provides backwards compatibility access for setting values to the request object. + * + * @param string $name + * @param mixed $value + * @return void + */ + public function __set($name, $value) { + switch ($name) { + case 'base': + case 'here': + case 'webroot': + case 'data': + return $this->request->{$name} = $value; + case 'action': + return $this->request->params['action'] = $value; + case 'params': + return $this->request->params = $value; + case 'paginate': + return $this->Components->load('Paginator')->settings = $value; + } + return $this->{$name} = $value; + } + +/** + * Sets the request objects and configures a number of controller properties + * based on the contents of the request. The properties that get set are + * + * - $this->request - To the $request parameter + * - $this->plugin - To the $request->params['plugin'] + * - $this->view - To the $request->params['action'] + * - $this->autoLayout - To the false if $request->params['bare']; is set. + * - $this->autoRender - To false if $request->params['return'] == 1 + * - $this->passedArgs - The the combined results of params['named'] and params['pass] + * + * @param CakeRequest $request + * @return void + */ + public function setRequest(CakeRequest $request) { + $this->request = $request; + $this->plugin = isset($request->params['plugin']) ? Inflector::camelize($request->params['plugin']) : null; + $this->view = isset($request->params['action']) ? $request->params['action'] : null; + if (isset($request->params['pass']) && isset($request->params['named'])) { + $this->passedArgs = array_merge($request->params['pass'], $request->params['named']); + } + + if (array_key_exists('return', $request->params) && $request->params['return'] == 1) { + $this->autoRender = false; + } + if (!empty($request->params['bare'])) { + $this->autoLayout = false; + } + } + +/** + * Dispatches the controller action. Checks that the action + * exists and isn't private. + * + * @param CakeRequest $request + * @return mixed The resulting response. + * @throws PrivateActionException, MissingActionException + */ + public function invokeAction(CakeRequest $request) { + $reflection = new ReflectionClass($this); + try { + $method = $reflection->getMethod($request->params['action']); + + if ($this->_isPrivateAction($method, $request)) { + throw new PrivateActionException(array( + 'controller' => $this->name . "Controller", + 'action' => $request->params['action'] + )); + } + return $method->invokeArgs($this, $request->params['pass']); + + } catch (ReflectionException $e) { + if ($this->scaffold !== false) { + return $this->_getScaffold($request); + } + throw new MissingActionException(array( + 'controller' => $this->name . "Controller", + 'action' => $request->params['action'] + )); + } + } + +/** + * Check if the request's action is marked as private, with an underscore, + * or if the request is attempting to directly accessing a prefixed action. + * + * @param ReflectionMethod $method The method to be invoked. + * @param CakeRequest $request The request to check. + * @return boolean + */ + protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) { + $privateAction = ( + $method->name[0] === '_' || + !$method->isPublic() || + !in_array($method->name, $this->methods) + ); + $prefixes = Router::prefixes(); + + if (!$privateAction && !empty($prefixes)) { + if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) { + list($prefix) = explode('_', $request->params['action']); + $privateAction = in_array($prefix, $prefixes); + } + } + return $privateAction; + } + +/** + * Returns a scaffold object to use for dynamically scaffolded controllers. + * + * @param CakeRequest $request + * @return Scaffold + */ + protected function _getScaffold(CakeRequest $request) { + return new Scaffold($this, $request); + } + +/** + * Merge components, helpers, and uses vars from Controller::$_mergeParent and PluginAppController. + * + * @return void + */ + protected function _mergeControllerVars() { + $pluginController = $pluginDot = null; + + if (!empty($this->plugin)) { + $pluginController = $this->plugin . 'AppController'; + if (!is_subclass_of($this, $pluginController)) { + $pluginController = null; + } + $pluginDot = $this->plugin . '.'; + } + + if (is_subclass_of($this, $this->_mergeParent) || !empty($pluginController)) { + $appVars = get_class_vars($this->_mergeParent); + $uses = $appVars['uses']; + $merge = array('components', 'helpers'); + + if ($uses == $this->uses && !empty($this->uses)) { + if (!in_array($pluginDot . $this->modelClass, $this->uses)) { + array_unshift($this->uses, $pluginDot . $this->modelClass); + } elseif ($this->uses[0] !== $pluginDot . $this->modelClass) { + $this->uses = array_flip($this->uses); + unset($this->uses[$pluginDot . $this->modelClass]); + $this->uses = array_flip($this->uses); + array_unshift($this->uses, $pluginDot . $this->modelClass); + } + } elseif ( + ($this->uses !== null || $this->uses !== false) && + is_array($this->uses) && !empty($appVars['uses']) + ) { + $this->uses = array_merge($this->uses, array_diff($appVars['uses'], $this->uses)); + } + $this->_mergeVars($merge, $this->_mergeParent, true); + } + + if ($pluginController && $this->plugin != null) { + $merge = array('components', 'helpers'); + $appVars = get_class_vars($pluginController); + if ( + ($this->uses !== null || $this->uses !== false) && + is_array($this->uses) && !empty($appVars['uses']) + ) { + $this->uses = array_merge($this->uses, array_diff($appVars['uses'], $this->uses)); + } + $this->_mergeVars($merge, $pluginController); + } + } + +/** + * Loads Model classes based on the uses property + * see Controller::loadModel(); for more info. + * Loads Components and prepares them for initialization. + * + * @return mixed true if models found and instance created. + * @see Controller::loadModel() + * @link http://book.cakephp.org/view/977/Controller-Methods#constructClasses-986 + * @throws MissingModelException + */ + public function constructClasses() { + $this->_mergeControllerVars(); + $this->Components->init($this); + if ($this->uses) { + $this->uses = (array) $this->uses; + list(, $this->modelClass) = pluginSplit(current($this->uses)); + } + return true; + } + +/** + * Perform the startup process for this controller. + * Fire the Components and Controller callbacks in the correct order. + * + * - Initializes components, which fires their `initialize` callback + * - Calls the controller `beforeFilter`. + * - triggers Component `startup` methods. + * + * @return void + */ + public function startupProcess() { + $this->Components->trigger('initialize', array(&$this)); + $this->beforeFilter(); + $this->Components->trigger('startup', array(&$this)); + } + +/** + * Perform the various shutdown processes for this controller. + * Fire the Components and Controller callbacks in the correct order. + * + * - triggers the component `shutdown` callback. + * - calls the Controller's `afterFilter` method. + * + * @return void + */ + public function shutdownProcess() { + $this->Components->trigger('shutdown', array(&$this)); + $this->afterFilter(); + } + +/** + * Queries & sets valid HTTP response codes & messages. + * + * @param mixed $code If $code is an integer, then the corresponding code/message is + * returned if it exists, null if it does not exist. If $code is an array, + * then the 'code' and 'message' keys of each nested array are added to the default + * HTTP codes. Example: + * + * httpCodes(404); // returns array(404 => 'Not Found') + * + * httpCodes(array( + * 701 => 'Unicorn Moved', + * 800 => 'Unexpected Minotaur' + * )); // sets these new values, and returns true + * + * @return mixed Associative array of the HTTP codes as keys, and the message + * strings as values, or null of the given $code does not exist. + * @deprecated Use CakeResponse::httpCodes(); + */ + public function httpCodes($code = null) { + return $this->response->httpCodes($code); + } + +/** + * Loads and instantiates models required by this controller. + * If the model is non existent, it will throw a missing database table error, as Cake generates + * dynamic models for the time being. + * + * @param string $modelClass Name of model class to load + * @param mixed $id Initial ID the instanced model class should have + * @return mixed true when single model found and instance created, error returned if model not found. + * @throws MissingModelException if the model class cannot be found. + */ + public function loadModel($modelClass = null, $id = null) { + if ($modelClass === null) { + $modelClass = $this->modelClass; + } + + $this->uses = ($this->uses) ? $this->uses : array(); + if (!in_array($modelClass, $this->uses)) { + $this->uses[] = $modelClass; + } + + list($plugin, $modelClass) = pluginSplit($modelClass, true); + + $this->{$modelClass} = ClassRegistry::init(array( + 'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id + )); + if (!$this->{$modelClass}) { + throw new MissingModelException($modelClass); + } + return true; + } + +/** + * Redirects to given $url, after turning off $this->autoRender. + * Script execution is halted after the redirect. + * + * @param mixed $url A string or array-based URL pointing to another location within the app, + * or an absolute URL + * @param integer $status Optional HTTP status code (eg: 404) + * @param boolean $exit If true, exit() will be called after the redirect + * @return mixed void if $exit = false. Terminates script if $exit = true + * @link http://book.cakephp.org/view/982/redirect + */ + public function redirect($url, $status = null, $exit = true) { + $this->autoRender = false; + + if (is_array($status)) { + extract($status, EXTR_OVERWRITE); + } + $response = $this->Components->trigger( + 'beforeRedirect', + array(&$this, $url, $status, $exit), + array('break' => true, 'breakOn' => false, 'collectReturn' => true) + ); + + if ($response === false) { + return; + } + extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE); + + $response = $this->beforeRedirect($url, $status, $exit); + if ($response === false) { + return; + } + extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE); + + if (function_exists('session_write_close')) { + session_write_close(); + } + + if (!empty($status) && is_string($status)) { + $codes = array_flip($this->response->httpCodes()); + if (isset($codes[$status])) { + $status = $codes[$status]; + } + } + + if ($url !== null) { + $this->response->header('Location', Router::url($url, true)); + } + + if (!empty($status) && ($status >= 300 && $status < 400)) { + $this->response->statusCode($status); + } + + if ($exit) { + $this->response->send(); + $this->_stop(); + } + } + +/** + * Parse beforeRedirect Response + * + * @param mixed $response Response from beforeRedirect callback + * @param mixed $url The same value of beforeRedirect + * @param integer $status The same value of beforeRedirect + * @param boolean $exit The same value of beforeRedirect + * @return array Array with keys url, status and exit + */ + protected function _parseBeforeRedirect($response, $url, $status, $exit) { + if (is_array($response)) { + foreach ($response as $resp) { + if (is_array($resp) && isset($resp['url'])) { + extract($resp, EXTR_OVERWRITE); + } elseif ($resp !== null) { + $url = $resp; + } + } + } + return compact('url', 'status', 'exit'); + } + +/** + * Convenience and object wrapper method for CakeResponse::header(). + * + * @param string $status The header message that is being set. + * @return void + * @deprecated Use CakeResponse::header() + */ + public function header($status) { + $this->response->header($status); + } + +/** + * Saves a variable for use inside a view template. + * + * @param mixed $one A string or an array of data. + * @param mixed $two Value in case $one is a string (which then works as the key). + * Unused if $one is an associative array, otherwise serves as the values to $one's keys. + * @return void + * @link http://book.cakephp.org/view/979/set + */ + public function set($one, $two = null) { + if (is_array($one)) { + if (is_array($two)) { + $data = array_combine($one, $two); + } else { + $data = $one; + } + } else { + $data = array($one => $two); + } + $this->viewVars = $data + $this->viewVars; + } + +/** + * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect() + * + * Examples: + * + * {{{ + * setAction('another_action'); + * setAction('action_with_parameters', $parameter1); + * }}} + * + * @param string $action The new action to be 'redirected' to + * @param mixed Any other parameters passed to this method will be passed as + * parameters to the new action. + * @return mixed Returns the return value of the called action + */ + public function setAction($action) { + $this->request->action = $action; + $args = func_get_args(); + unset($args[0]); + return call_user_func_array(array(&$this, $action), $args); + } + +/** + * Returns number of errors in a submitted FORM. + * + * @return integer Number of errors + */ + public function validate() { + $args = func_get_args(); + $errors = call_user_func_array(array(&$this, 'validateErrors'), $args); + + if ($errors === false) { + return 0; + } + return count($errors); + } + +/** + * Validates models passed by parameters. Example: + * + * `$errors = $this->validateErrors($this->Article, $this->User);` + * + * @param mixed A list of models as a variable argument + * @return array Validation errors, or false if none + */ + public function validateErrors() { + $objects = func_get_args(); + + if (empty($objects)) { + return false; + } + + $errors = array(); + foreach ($objects as $object) { + if (isset($this->{$object->alias})) { + $object = $this->{$object->alias}; + } + $object->set($object->data); + $errors = array_merge($errors, $object->invalidFields()); + } + + return $this->validationErrors = (!empty($errors) ? $errors : false); + } + +/** + * Instantiates the correct view class, hands it its data, and uses it to render the view output. + * + * @param string $view View to use for rendering + * @param string $layout Layout to use + * @return CakeResponse A response object containing the rendered view. + * @link http://book.cakephp.org/view/980/render + */ + public function render($view = null, $layout = null) { + $this->beforeRender(); + $this->Components->trigger('beforeRender', array(&$this)); + + $viewClass = $this->viewClass; + if ($this->viewClass != 'View') { + list($plugin, $viewClass) = pluginSplit($viewClass, true); + $viewClass = $viewClass . 'View'; + App::uses($viewClass, $plugin . 'View'); + } + + $View = new $viewClass($this); + + if (!empty($this->uses)) { + foreach ($this->uses as $model) { + list($plugin, $className) = pluginSplit($model); + $this->request->params['models'][$className] = compact('plugin', 'className'); + } + } if (!empty($this->modelClass) && ($this->uses === false || $this->uses === array())) { + $this->request->params['models'][$this->modelClass] = array('plugin' => $this->plugin, 'className' => $this->modelClass); + } + + $models = ClassRegistry::keys(); + foreach ($models as $currentModel) { + $currentObject = ClassRegistry::getObject($currentModel); + if (is_a($currentObject, 'Model')) { + $className = get_class($currentObject); + list($plugin) = pluginSplit(App::location($className)); + $this->request->params['models'][$currentObject->alias] = compact('plugin', 'className'); + $View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors; + } + } + + $this->autoRender = false; + $this->View = $View; + $this->response->body($View->render($view, $layout)); + return $this->response; + } + +/** + * Returns the referring URL for this request. + * + * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers + * @param boolean $local If true, restrict referring URLs to local server + * @return string Referring URL + * @link http://book.cakephp.org/view/987/referer + */ + public function referer($default = null, $local = false) { + if ($this->request) { + $referer = $this->request->referer($local); + if ($referer == '/' && $default != null) { + return Router::url($default, true); + } + return $referer; + } + return '/'; + } + +/** + * Forces the user's browser not to cache the results of the current request. + * + * @return void + * @link http://book.cakephp.org/view/988/disableCache + * @deprecated Use CakeResponse::disableCache() + */ + public function disableCache() { + $this->response->disableCache(); + } + +/** + * Shows a message to the user for $pause seconds, then redirects to $url. + * Uses flash.ctp as the default layout for the message. + * Does not work if the current debug level is higher than 0. + * + * @param string $message Message to display to the user + * @param mixed $url Relative string or array-based URL to redirect to after the time expires + * @param integer $pause Time to show the message + * @param string $layout Layout you want to use, defaults to 'flash' + * @return void Renders flash layout + * @link http://book.cakephp.org/view/983/flash + */ + public function flash($message, $url, $pause = 1, $layout = 'flash') { + $this->autoRender = false; + $this->set('url', Router::url($url)); + $this->set('message', $message); + $this->set('pause', $pause); + $this->set('page_title', $message); + $this->render(false, $layout); + } + +/** + * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call. + * + * @param array $data POST'ed data organized by model and field + * @param mixed $op A string containing an SQL comparison operator, or an array matching operators + * to fields + * @param string $bool SQL boolean operator: AND, OR, XOR, etc. + * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be + * included in the returned conditions + * @return array An array of model conditions + * @link http://book.cakephp.org/view/989/postConditions + */ + public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) { + if (!is_array($data) || empty($data)) { + if (!empty($this->request->data)) { + $data = $this->request->data; + } else { + return null; + } + } + $cond = array(); + + if ($op === null) { + $op = ''; + } + + $arrayOp = is_array($op); + foreach ($data as $model => $fields) { + foreach ($fields as $field => $value) { + $key = $model.'.'.$field; + $fieldOp = $op; + if ($arrayOp) { + if (array_key_exists($key, $op)) { + $fieldOp = $op[$key]; + } elseif (array_key_exists($field, $op)) { + $fieldOp = $op[$field]; + } else { + $fieldOp = false; + } + } + if ($exclusive && $fieldOp === false) { + continue; + } + $fieldOp = strtoupper(trim($fieldOp)); + if ($fieldOp === 'LIKE') { + $key = $key.' LIKE'; + $value = '%'.$value.'%'; + } elseif ($fieldOp && $fieldOp != '=') { + $key = $key.' '.$fieldOp; + } + $cond[$key] = $value; + } + } + if ($bool != null && strtoupper($bool) != 'AND') { + $cond = array($bool => $cond); + } + return $cond; + } + +/** + * Handles automatic pagination of model records. + * + * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel') + * @param mixed $scope Conditions to use while paginating + * @param array $whitelist List of allowed options for paging + * @return array Model query results + * @link http://book.cakephp.org/view/1232/Controller-Setup + * @deprecated Use PaginatorComponent instead + */ + public function paginate($object = null, $scope = array(), $whitelist = array()) { + return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist); + } + +/** + * Called before the controller action. You can use this method to configure and customize components + * or perform logic that needs to happen before each controller action. + * + * @return void + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function beforeFilter() { + } + +/** + * Called after the controller action is run, but before the view is rendered. You can use this method + * to perform logic or set view variables that are required on every request. + * + * @return void + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function beforeRender() { + } + +/** + * The beforeRedirect method is invoked when the controller's redirect method is called but before any + * further action. If this method returns false the controller will not continue on to redirect the request. + * The $url, $status and $exit variables have same meaning as for the controller's method. You can also + * return a string which will be interpreted as the url to redirect to or return associative array with + * key 'url' and optionally 'status' and 'exit'. + * + * @param mixed $url A string or array-based URL pointing to another location within the app, + * or an absolute URL + * @param integer $status Optional HTTP status code (eg: 404) + * @param boolean $exit If true, exit() will be called after the redirect + * @return boolean + */ + public function beforeRedirect($url, $status = null, $exit = true) { + return true; + } + +/** + * Called after the controller action is run and rendered. + * + * @return void + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function afterFilter() { + } + +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called example index, edit, etc. + * @return boolean Success + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function beforeScaffold($method) { + return true; + } + +/** + * Alias to beforeScaffold() + * + * @param string $method + * @return boolean + * @see Controller::beforeScaffold() + * @deprecated + */ + protected function _beforeScaffold($method) { + return $this->beforeScaffold($method); + } + +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called either edit or update. + * @return boolean Success + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function afterScaffoldSave($method) { + return true; + } + +/** + * Alias to afterScaffoldSave() + * + * @param string $method + * @return boolean + * @see Controller::afterScaffoldSave() + * @deprecated + */ + protected function _afterScaffoldSave($method) { + return $this->afterScaffoldSave($method); + } + +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called either edit or update. + * @return boolean Success + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function afterScaffoldSaveError($method) { + return true; + } + +/** + * Alias to afterScaffoldSaveError() + * + * @param string $method + * @return boolean + * @see Controller::afterScaffoldSaveError() + * @deprecated + */ + protected function _afterScaffoldSaveError($method) { + return $this->afterScaffoldSaveError($method); + } + +/** + * This method should be overridden in child classes. + * If not it will render a scaffold error. + * Method MUST return true in child classes + * + * @param string $method name of method called example index, edit, etc. + * @return boolean Success + * @link http://book.cakephp.org/view/984/Callbacks + */ + public function scaffoldError($method) { + return false; + } + +/** + * Alias to scaffoldError() + * + * @param string $method + * @return boolean + * @see Controller::scaffoldError() + * @deprecated + */ + protected function _scaffoldError($method) { + return $this->scaffoldError($method); + } + +} diff --git a/app/Cake/Controller/PagesController.php b/app/Cake/Controller/PagesController.php new file mode 100644 index 00000000..bd6a3e7d --- /dev/null +++ b/app/Cake/Controller/PagesController.php @@ -0,0 +1,82 @@ +redirect('/'); + } + $page = $subpage = $title_for_layout = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + if (!empty($path[$count - 1])) { + $title_for_layout = Inflector::humanize($path[$count - 1]); + } + $this->set(compact('page', 'subpage', 'title_for_layout')); + $this->render(implode('/', $path)); + } +} diff --git a/app/Cake/Controller/Scaffold.php b/app/Cake/Controller/Scaffold.php new file mode 100644 index 00000000..db781dd9 --- /dev/null +++ b/app/Cake/Controller/Scaffold.php @@ -0,0 +1,443 @@ +controller = $controller; + + $count = count($this->_passedVars); + for ($j = 0; $j < $count; $j++) { + $var = $this->_passedVars[$j]; + $this->{$var} = $controller->{$var}; + } + + $this->redirect = array('action' => 'index'); + + $this->modelClass = $controller->modelClass; + $this->modelKey = $controller->modelKey; + + if (!is_object($this->controller->{$this->modelClass})) { + throw new MissingModelException($this->modelClass); + } + + $this->ScaffoldModel = $this->controller->{$this->modelClass}; + $this->scaffoldTitle = Inflector::humanize(Inflector::underscore($this->viewPath)); + $this->scaffoldActions = $controller->scaffold; + $title_for_layout = __d('cake', 'Scaffold :: ') . Inflector::humanize($request->action) . ' :: ' . $this->scaffoldTitle; + $modelClass = $this->controller->modelClass; + $primaryKey = $this->ScaffoldModel->primaryKey; + $displayField = $this->ScaffoldModel->displayField; + $singularVar = Inflector::variable($modelClass); + $pluralVar = Inflector::variable($this->controller->name); + $singularHumanName = Inflector::humanize(Inflector::underscore($modelClass)); + $pluralHumanName = Inflector::humanize(Inflector::underscore($this->controller->name)); + $scaffoldFields = array_keys($this->ScaffoldModel->schema()); + $associations = $this->_associations(); + + $this->controller->set(compact( + 'title_for_layout', 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', + 'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations' + )); + + if ($this->controller->viewClass) { + $this->controller->viewClass = 'Scaffold'; + } + $this->_validSession = ( + isset($this->controller->Session) && $this->controller->Session->valid() != false + ); + $this->_scaffold($request); + } + +/** + * Renders a view action of scaffolded model. + * + * @param CakeRequest $request Request Object for scaffolding + * @return mixed A rendered view of a row from Models database table + * @throws NotFoundException + */ + protected function _scaffoldView(CakeRequest $request) { + if ($this->controller->beforeScaffold('view')) { + if (isset($request->params['pass'][0])) { + $this->ScaffoldModel->id = $request->params['pass'][0]; + } + if (!$this->ScaffoldModel->exists()) { + throw new NotFoundException(__d('cake', 'Invalid %s', Inflector::humanize($this->modelKey))); + } + $this->ScaffoldModel->recursive = 1; + $this->controller->request->data = $this->ScaffoldModel->read(); + $this->controller->set( + Inflector::variable($this->controller->modelClass), $this->request->data + ); + $this->controller->render($this->request['action'], $this->layout); + } elseif ($this->controller->scaffoldError('view') === false) { + return $this->_scaffoldError(); + } + } + +/** + * Renders index action of scaffolded model. + * + * @param array $params Parameters for scaffolding + * @return mixed A rendered view listing rows from Models database table + */ + protected function _scaffoldIndex($params) { + if ($this->controller->beforeScaffold('index')) { + $this->ScaffoldModel->recursive = 0; + $this->controller->set( + Inflector::variable($this->controller->name), $this->controller->paginate() + ); + $this->controller->render($this->request['action'], $this->layout); + } elseif ($this->controller->scaffoldError('index') === false) { + return $this->_scaffoldError(); + } + } + +/** + * Renders an add or edit action for scaffolded model. + * + * @param string $action Action (add or edit) + * @return mixed A rendered view with a form to edit or add a record in the Models database table + */ + protected function _scaffoldForm($action = 'edit') { + $this->controller->viewVars['scaffoldFields'] = array_merge( + $this->controller->viewVars['scaffoldFields'], + array_keys($this->ScaffoldModel->hasAndBelongsToMany) + ); + $this->controller->render($action, $this->layout); + } + +/** + * Saves or updates the scaffolded model. + * + * @param CakeRequest $request Request Object for scaffolding + * @param string $action add or edt + * @return mixed Success on save/update, add/edit form if data is empty or error if save or update fails + * @throws NotFoundException + */ + protected function _scaffoldSave(CakeRequest $request, $action = 'edit') { + $formAction = 'edit'; + $success = __d('cake', 'updated'); + if ($action === 'add') { + $formAction = 'add'; + $success = __d('cake', 'saved'); + } + + if ($this->controller->beforeScaffold($action)) { + if ($action == 'edit') { + if (isset($request->params['pass'][0])) { + $this->ScaffoldModel->id = $request['pass'][0]; + } + if (!$this->ScaffoldModel->exists()) { + throw new NotFoundException(__d('cake', 'Invalid %s', Inflector::humanize($this->modelKey))); + } + } + + if (!empty($request->data)) { + if ($action == 'create') { + $this->ScaffoldModel->create(); + } + + if ($this->ScaffoldModel->save($request->data)) { + if ($this->controller->afterScaffoldSave($action)) { + $message = __d('cake', + 'The %1$s has been %2$s', + Inflector::humanize($this->modelKey), + $success + ); + return $this->_sendMessage($message); + } else { + return $this->controller->afterScaffoldSaveError($action); + } + } else { + if ($this->_validSession) { + $this->controller->Session->setFlash(__d('cake', 'Please correct errors below.')); + } + } + } + + if (empty($request->data)) { + if ($this->ScaffoldModel->id) { + $this->controller->data = $request->data = $this->ScaffoldModel->read(); + } else { + $this->controller->data = $request->data = $this->ScaffoldModel->create(); + } + } + + foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) { + $varName = Inflector::variable(Inflector::pluralize( + preg_replace('/(?:_id)$/', '', $assocData['foreignKey']) + )); + $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list')); + } + foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) { + $varName = Inflector::variable(Inflector::pluralize($assocName)); + $this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list')); + } + + return $this->_scaffoldForm($formAction); + } elseif ($this->controller->scaffoldError($action) === false) { + return $this->_scaffoldError(); + } + } + +/** + * Performs a delete on given scaffolded Model. + * + * @param CakeRequest $request Request for scaffolding + * @return mixed Success on delete, error if delete fails + * @throws MethodNotAllowedException, NotFoundException + */ + protected function _scaffoldDelete(CakeRequest $request) { + if ($this->controller->beforeScaffold('delete')) { + if (!$request->is('post')) { + throw new MethodNotAllowedException(); + } + $id = false; + if (isset($request->params['pass'][0])) { + $id = $request->params['pass'][0]; + } + $this->ScaffoldModel->id = $id; + if (!$this->ScaffoldModel->exists()) { + throw new NotFoundException(__d('cake', 'Invalid %s', Inflector::humanize($this->modelClass))); + } + if ($this->ScaffoldModel->delete()) { + $message = __d('cake', 'The %1$s with id: %2$d has been deleted.', Inflector::humanize($this->modelClass), $id); + return $this->_sendMessage($message); + } else { + $message = __d('cake', + 'There was an error deleting the %1$s with id: %2$d', + Inflector::humanize($this->modelClass), + $id + ); + return $this->_sendMessage($message); + } + } elseif ($this->controller->scaffoldError('delete') === false) { + return $this->_scaffoldError(); + } + } + +/** + * Sends a message to the user. Either uses Sessions or flash messages depending + * on the availability of a session + * + * @param string $message Message to display + * @return void + */ + protected function _sendMessage($message) { + if ($this->_validSession) { + $this->controller->Session->setFlash($message); + $this->controller->redirect($this->redirect); + } else { + $this->controller->flash($message, $this->redirect); + } + } + +/** + * Show a scaffold error + * + * @return mixed A rendered view showing the error + */ + protected function _scaffoldError() { + return $this->controller->render('error', $this->layout); + } + +/** + * When methods are now present in a controller + * scaffoldView is used to call default Scaffold methods if: + * `public $scaffold;` is placed in the controller's class definition. + * + * @param CakeRequest $request Request object for scaffolding + * @return mixed A rendered view of scaffold action, or showing the error + * @throws MissingActionException, MissingDatabaseException + */ + protected function _scaffold(CakeRequest $request) { + $db = ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig); + $prefixes = Configure::read('Routing.prefixes'); + $scaffoldPrefix = $this->scaffoldActions; + + if (isset($db)) { + if (empty($this->scaffoldActions)) { + $this->scaffoldActions = array( + 'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete' + ); + } elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) { + $this->scaffoldActions = array( + $scaffoldPrefix . '_index', + $scaffoldPrefix . '_list', + $scaffoldPrefix . '_view', + $scaffoldPrefix . '_add', + $scaffoldPrefix . '_create', + $scaffoldPrefix . '_edit', + $scaffoldPrefix . '_update', + $scaffoldPrefix . '_delete' + ); + } + + if (in_array($request->params['action'], $this->scaffoldActions)) { + if (!empty($prefixes)) { + $request->params['action'] = str_replace($scaffoldPrefix . '_', '', $request->params['action']); + } + switch ($request->params['action']) { + case 'index': + case 'list': + $this->_scaffoldIndex($request); + break; + case 'view': + $this->_scaffoldView($request); + break; + case 'add': + case 'create': + $this->_scaffoldSave($request, 'add'); + break; + case 'edit': + case 'update': + $this->_scaffoldSave($request, 'edit'); + break; + case 'delete': + $this->_scaffoldDelete($request); + break; + } + } else { + throw new MissingActionException(array( + 'controller' => $this->controller->name, + 'action' => $request->action + )); + } + } else { + throw new MissingDatabaseException(array('connection' => $this->ScaffoldModel->useDbConfig)); + } + } + +/** + * Returns associations for controllers models. + * + * @return array Associations for model + */ + protected function _associations() { + $keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + $associations = array(); + + foreach ($keys as $key => $type) { + foreach ($this->ScaffoldModel->{$type} as $assocKey => $assocData) { + $associations[$type][$assocKey]['primaryKey'] = + $this->ScaffoldModel->{$assocKey}->primaryKey; + + $associations[$type][$assocKey]['displayField'] = + $this->ScaffoldModel->{$assocKey}->displayField; + + $associations[$type][$assocKey]['foreignKey'] = + $assocData['foreignKey']; + + $associations[$type][$assocKey]['controller'] = + Inflector::pluralize(Inflector::underscore($assocData['className'])); + + if ($type == 'hasAndBelongsToMany') { + $associations[$type][$assocKey]['with'] = $assocData['with']; + } + } + } + return $associations; + } +} diff --git a/app/Cake/Core/App.php b/app/Cake/Core/App.php new file mode 100644 index 00000000..225b72bd --- /dev/null +++ b/app/Cake/Core/App.php @@ -0,0 +1,862 @@ + array('extends' => null, 'core' => true), + 'file' => array('extends' => null, 'core' => true), + 'model' => array('extends' => 'AppModel', 'core' => false), + 'behavior' => array('suffix' => 'Behavior', 'extends' => 'Model/ModelBehavior', 'core' => true), + 'controller' => array('suffix' => 'Controller', 'extends' => 'AppController', 'core' => true), + 'component' => array('suffix' => 'Component', 'extends' => null, 'core' => true), + 'lib' => array('extends' => null, 'core' => true), + 'view' => array('suffix' => 'View', 'extends' => null, 'core' => true), + 'helper' => array('suffix' => 'Helper', 'extends' => 'AppHelper', 'core' => true), + 'vendor' => array('extends' => null, 'core' => true), + 'shell' => array('suffix' => 'Shell', 'extends' => 'Shell', 'core' => true), + 'plugin' => array('extends' => null, 'core' => true) + ); + +/** + * Paths to search for files. + * + * @var array + */ + public static $search = array(); + +/** + * Whether or not to return the file that is loaded. + * + * @var boolean + */ + public static $return = false; + +/** + * Holds key/value pairs of $type => file path. + * + * @var array + */ + protected static $_map = array(); + +/** + * Holds and key => value array of object types. + * + * @var array + */ + protected static $_objects = array(); + +/** + * Holds the location of each class + * + * @var array + */ + protected static $_classMap = array(); + +/** + * Holds the possible paths for each package name + * + * @var array + */ + protected static $_packages = array(); + +/** + * Holds the templates for each customizable package path in the application + * + * @var array + */ + protected static $_packageFormat = array(); + +/** + * Maps an old style CakePHP class type to the corresponding package + * + * @var array + */ + public static $legacy = array( + 'models' => 'Model', + 'behaviors' => 'Model/Behavior', + 'datasources' => 'Model/Datasource', + 'controllers' => 'Controller', + 'components' => 'Controller/Component', + 'views' => 'View', + 'helpers' => 'View/Helper', + 'shells' => 'Console/Command', + 'libs' => 'Lib' + ); + +/** + * Indicates whether the class cache should be stored again because of an addition to it + * + * @var boolean + */ + protected static $_cacheChange = false; + +/** + * Indicates whether the object cache should be stored again because of an addition to it + * + * @var boolean + */ + protected static $_objectCacheChange = false; + +/** + * Indicates the the Application is in the bootstrapping process. Used to better cache + * loaded classes while the cache libraries have not been yet initialized + * + * @var boolean + */ + public static $bootstrapping = false; + +/** + * Used to read information stored path + * + * Usage: + * + * `App::path('Model'); will return all paths for models` + * + * `App::path('Model/Datasource', 'MyPlugin'); will return the path for datasources under the 'MyPlugin' plugin` + * + * @param string $type type of path + * @param string $plugin name of plugin + * @return string array + */ + public static function path($type, $plugin = null) { + if (!empty(self::$legacy[$type])) { + $type = self::$legacy[$type]; + } + + if (!empty($plugin)) { + $path = array(); + $pluginPath = self::pluginPath($plugin); + if (!empty(self::$_packageFormat[$type])) { + foreach (self::$_packageFormat[$type] as $f) { + $path[] = sprintf($f, $pluginPath); + } + } + $path[] = $pluginPath . 'Lib' . DS . $type . DS; + return $path; + } + + if (!isset(self::$_packages[$type])) { + return array(); + } + return self::$_packages[$type]; + } + +/** + * Get all the currently loaded paths from App. Useful for inspecting + * or storing all paths App knows about. For a paths to a specific package + * use App::path() + * + * @return array An array of packages and their associated paths. + */ + public static function paths() { + return self::$_packages; + } + +/** + * Sets up each package location on the file system. You can configure multiple search paths + * for each package, those will be used to look for files one folder at a time in the specified order + * All paths should be terminated with a Directory separator + * + * Usage: + * + * `App::build(array(Model' => array('/a/full/path/to/models/'))); will setup a new search path for the Model package` + * + * `App::build(array('Model' => array('/path/to/models/')), App::RESET); will setup the path as the only valid path for searching models` + * + * `App::build(array('View/Helper' => array('/path/to/helpers/', '/another/path/))); will setup multiple search paths for helpers` + * + * If reset is set to true, all loaded plugins will be forgotten and they will be needed to be loaded again. + * + * @param array $paths associative array with package names as keys and a list of directories for new search paths + * @param mixed $mode App::RESET will set paths, App::APPEND with append paths, App::PREPEND will prepend paths, [default] App::PREPEND + * @return void + */ + public static function build($paths = array(), $mode = App::PREPEND) { + if (empty(self::$_packageFormat)) { + self::$_packageFormat = array( + 'Model' => array( + '%s' . 'Model' . DS, + '%s' . 'models' . DS + ), + 'Model/Behavior' => array( + '%s' . 'Model' . DS . 'Behavior' . DS, + '%s' . 'models' . DS . 'behaviors' . DS + ), + 'Model/Datasource' => array( + '%s' . 'Model' . DS . 'Datasource' . DS, + '%s' . 'models' . DS . 'datasources' . DS + ), + 'Model/Datasource/Database' => array( + '%s' . 'Model' . DS . 'Datasource' . DS . 'Database' . DS, + '%s' . 'models' . DS . 'datasources' . DS . 'database' . DS + ), + 'Model/Datasource/Session' => array( + '%s' . 'Model' . DS . 'Datasource' . DS . 'Session' . DS, + '%s' . 'models' . DS . 'datasources' . DS . 'session' . DS + ), + 'Controller' => array( + '%s' . 'Controller' . DS, + '%s' . 'controllers' . DS + ), + 'Controller/Component' => array( + '%s' . 'Controller' . DS . 'Component' . DS, + '%s' . 'controllers' . DS . 'components' . DS + ), + 'Controller/Component/Auth' => array( + '%s' . 'Controller' . DS . 'Component' . DS . 'Auth' . DS, + '%s' . 'controllers' . DS . 'components' . DS . 'auth' . DS + ), + 'View' => array( + '%s' . 'View' . DS, + '%s' . 'views' . DS + ), + 'View/Helper' => array( + '%s' . 'View' . DS . 'Helper' . DS, + '%s' . 'views' . DS . 'helpers' . DS + ), + 'Console' => array( + '%s' . 'Console' . DS, + '%s' . 'console' . DS + ), + 'Console/Command' => array( + '%s' . 'Console' . DS . 'Command' . DS, + '%s' . 'console' . DS . 'shells' . DS, + ), + 'Console/Command/Task' => array( + '%s' . 'Console' . DS . 'Command' . DS . 'Task' . DS, + '%s' . 'console' . DS . 'shells' . DS . 'tasks' . DS + ), + 'Lib' => array( + '%s' . 'Lib' . DS, + '%s' . 'libs' . DS + ), + 'locales' => array( + '%s' . 'Locale' . DS, + '%s' . 'locale' . DS + ), + 'vendors' => array('%s' . 'Vendor' . DS, VENDORS), + 'plugins' => array( + APP . 'Plugin' . DS, + APP . 'plugins' . DS, + dirname(dirname(CAKE)) . DS . 'plugins' . DS, + ) + ); + } + + if ($mode === App::RESET) { + foreach ($paths as $type => $new) { + if (!empty(self::$legacy[$type])) { + $type = self::$legacy[$type]; + } + self::$_packages[$type] = (array)$new; + self::objects($type, null, false); + } + return $paths; + } + + //Provides Backwards compatibility for old-style package names + $legacyPaths = array(); + foreach ($paths as $type => $path) { + if (!empty(self::$legacy[$type])) { + $type = self::$legacy[$type]; + } + $legacyPaths[$type] = $path; + } + + $paths = $legacyPaths; + $defaults = array(); + foreach (self::$_packageFormat as $package => $format) { + foreach ($format as $f) { + $defaults[$package][] = sprintf($f, APP); + } + } + + foreach ($defaults as $type => $default) { + if (empty(self::$_packages[$type]) || empty($paths)) { + self::$_packages[$type] = $default; + } + + if (!empty($paths[$type])) { + if ($mode === App::PREPEND) { + $path = array_merge((array)$paths[$type], self::$_packages[$type]); + } else { + $path = array_merge(self::$_packages[$type], (array)$paths[$type]); + } + } else { + $path = self::$_packages[$type]; + } + + self::$_packages[$type] = array_values(array_unique($path)); + } + } + +/** + * Gets the path that a plugin is on. Searches through the defined plugin paths. + * + * Usage: + * + * `App::pluginPath('MyPlugin'); will return the full path to 'MyPlugin' plugin'` + * + * @param string $plugin CamelCased/lower_cased plugin name to find the path of. + * @return string full path to the plugin. + */ + public static function pluginPath($plugin) { + return CakePlugin::path($plugin); + } + +/** + * Finds the path that a theme is on. Searches through the defined theme paths. + * + * Usage: + * + * `App::themePath('MyTheme'); will return the full path to the 'MyTheme' theme` + * + * @param string $theme theme name to find the path of. + * @return string full path to the theme. + */ + public static function themePath($theme) { + $themeDir = 'Themed' . DS . Inflector::camelize($theme); + foreach (self::$_packages['View'] as $path) { + if (is_dir($path . $themeDir)) { + return $path . $themeDir . DS ; + } + } + return self::$_packages['View'][0] . $themeDir . DS; + } + +/** + * Returns the full path to a package inside the CakePHP core + * + * Usage: + * + * `App::core('Cache/Engine'); will return the full path to the cache engines package` + * + * @param string $type + * @return string full path to package + */ + public static function core($type) { + return array(CAKE . str_replace('/', DS, $type) . DS); + } + +/** + * Returns an array of objects of the given type. + * + * Example usage: + * + * `App::objects('plugin');` returns `array('DebugKit', 'Blog', 'User');` + * + * `App::objects('Controller');` returns `array('PagesController', 'BlogController');` + * + * You can also search only within a plugin's objects by using the plugin dot + * syntax. + * + * `App::objects('MyPlugin.Model');` returns `array('MyPluginPost', 'MyPluginComment');` + * + * When scanning directories, files and directories beginning with `.` will be excluded as these + * are commonly used by version control systems. + * + * @param string $type Type of object, i.e. 'Model', 'Controller', 'View/Helper', 'file', 'class' or 'plugin' + * @param mixed $path Optional Scan only the path given. If null, paths for the chosen type will be used. + * @param boolean $cache Set to false to rescan objects of the chosen type. Defaults to true. + * @return mixed Either false on incorrect / miss. Or an array of found objects. + */ + public static function objects($type, $path = null, $cache = true) { + $extension = '/\.php$/'; + $includeDirectories = false; + $name = $type; + + if ($type === 'plugin') { + $type = 'plugins'; + } + + if ($type == 'plugins') { + $extension = '/.*/'; + $includeDirectories = true; + } + + list($plugin, $type) = pluginSplit($type); + + if (isset(self::$legacy[$type . 's'])) { + $type = self::$legacy[$type . 's']; + } + + if ($type === 'file' && !$path) { + return false; + } elseif ($type === 'file') { + $extension = '/\.php$/'; + $name = $type . str_replace(DS, '', $path); + } + + if (empty(self::$_objects) && $cache === true) { + self::$_objects = Cache::read('object_map', '_cake_core_'); + } + + $cacheLocation = empty($plugin) ? 'app' : $plugin; + + if ($cache !== true || !isset(self::$_objects[$cacheLocation][$name])) { + $objects = array(); + + if (empty($path)) { + $path = self::path($type, $plugin); + } + + foreach ((array)$path as $dir) { + if ($dir != APP && is_dir($dir)) { + $files = new RegexIterator(new DirectoryIterator($dir), $extension); + foreach ($files as $file) { + $fileName = basename($file); + if (!$file->isDot() && $fileName[0] !== '.') { + $isDir = $file->isDir() ; + if ($isDir && $includeDirectories) { + $objects[] = $fileName; + } elseif (!$includeDirectories && !$isDir) { + $objects[] = substr($fileName, 0, -4); + } + } + } + } + } + + if ($type !== 'file') { + foreach ($objects as $key => $value) { + $objects[$key] = Inflector::camelize($value); + } + } + + sort($objects); + if ($plugin) { + return $objects; + } + + self::$_objects[$cacheLocation][$name] = $objects; + if ($cache) { + self::$_objectCacheChange = true; + } + } + + return self::$_objects[$cacheLocation][$name]; + } + +/** + * Declares a package for a class. This package location will be used + * by the automatic class loader if the class is tried to be used + * + * Usage: + * + * `App::uses('MyCustomController', 'Controller');` will setup the class to be found under Controller package + * + * `App::uses('MyHelper', 'MyPlugin.View/Helper');` will setup the helper class to be found in plugin's helper package + * + * @param string $className the name of the class to configure package for + * @param string $location the package name + * @return void + */ + public static function uses($className, $location) { + self::$_classMap[$className] = $location; + } + +/** + * Method to handle the automatic class loading. It will look for each class' package + * defined using App::uses() and with this information it will resolve the package name to a full path + * to load the class from. File name for each class should follow the class name. For instance, + * if a class is name `MyCustomClass` the file name should be `MyCustomClass.php` + * + * @param string $className the name of the class to load + * @return boolean + */ + public static function load($className) { + if (!isset(self::$_classMap[$className])) { + return false; + } + + if ($file = self::_mapped($className)) { + return include $file; + } + + $parts = explode('.', self::$_classMap[$className], 2); + list($plugin, $package) = count($parts) > 1 ? $parts : array(null, current($parts)); + $paths = self::path($package, $plugin); + + if (empty($plugin)) { + $appLibs = empty(self::$_packages['Lib']) ? APPLIBS : current(self::$_packages['Lib']); + $paths[] = $appLibs . $package . DS; + $paths[] = CAKE . $package . DS; + } + + foreach ($paths as $path) { + $file = $path . $className . '.php'; + if (file_exists($file)) { + self::_map($file, $className, $plugin); + return include $file; + } + } + + //To help apps migrate to 2.0 old style file names are allowed + foreach ($paths as $path) { + $underscored = Inflector::underscore($className); + $tries = array($path . $underscored . '.php'); + $parts = explode('_', $underscored); + if (count($parts) > 1) { + array_pop($parts); + $tries[] = $path . implode('_', $parts) . '.php'; + } + foreach ($tries as $file) { + if (file_exists($file)) { + self::_map($file, $className); + return include $file; + } + } + } + + return false; + } + +/** + * Returns the package name where a class was defined to be located at + * + * @param string $className name of the class to obtain the package name from + * @return string package name or null if not declared + */ + public static function location($className) { + if (!empty(self::$_classMap[$className])) { + return self::$_classMap[$className]; + } + return null; + } + +/** + * Finds classes based on $name or specific file(s) to search. Calling App::import() will + * not construct any classes contained in the files. It will only find and require() the file. + * + * @link http://book.cakephp.org/view/934/Using-App-import + * @param mixed $type The type of Class if passed as a string, or all params can be passed as + * an single array to $type, + * @param string $name Name of the Class or a unique name for the file + * @param mixed $parent boolean true if Class Parent should be searched, accepts key => value + * array('parent' => $parent ,'file' => $file, 'search' => $search, 'ext' => '$ext'); + * $ext allows setting the extension of the file name + * based on Inflector::underscore($name) . ".$ext"; + * @param array $search paths to search for files, array('path 1', 'path 2', 'path 3'); + * @param string $file full name of the file to search for including extension + * @param boolean $return, return the loaded file, the file must have a return + * statement in it to work: return $variable; + * @return boolean true if Class is already in memory or if file is found and loaded, false if not + */ + public static function import($type = null, $name = null, $parent = true, $search = array(), $file = null, $return = false) { + $ext = null; + + if (is_array($type)) { + extract($type, EXTR_OVERWRITE); + } + + if (is_array($parent)) { + extract($parent, EXTR_OVERWRITE); + } + + if ($name == null && $file == null) { + return false; + } + + if (is_array($name)) { + foreach ($name as $class) { + if (!App::import(compact('type', 'parent', 'search', 'file', 'return') + array('name' => $class))) { + return false; + } + } + return true; + } + + $originalType = strtolower($type); + $specialPackage = in_array($originalType, array('file', 'vendor')); + if (!$specialPackage && isset(self::$legacy[$originalType . 's'])) { + $type = self::$legacy[$originalType . 's']; + } + list($plugin, $name) = pluginSplit($name); + if (!empty($plugin)) { + if (!CakePlugin::loaded($plugin)) { + return false; + } + } + + if (!$specialPackage) { + return self::_loadClass($name, $plugin, $type, $originalType, $parent); + } + + if ($originalType == 'file' && !empty($file)) { + return self::_loadFile($name, $plugin, $search, $file, $return); + } + + if ($originalType == 'vendor') { + return self::_loadVendor($name, $plugin, $file, $ext); + } + + return false; + } + +/** + * Helper function to include classes + * This is a compatibility wrapper around using App::uses() and automatic class loading + * + * @param string $name unique name of the file for identifying it inside the application + * @param string $plugin camel cased plugin name if any + * @param string $type name of the packed where the class is located + * @param string $originalType type name as supplied initially by the user + * @param boolean $parent whether to load the class parent or not + * @return boolean true indicating the successful load and existence of the class + */ + protected static function _loadClass($name, $plugin, $type, $originalType, $parent) { + if ($type == 'Console/Command' && $name == 'Shell') { + $type = 'Console'; + } else if (isset(self::$types[$originalType]['suffix'])) { + $suffix = self::$types[$originalType]['suffix']; + $name .= ($suffix == $name) ? '' : $suffix; + } + if ($parent && isset(self::$types[$originalType]['extends'])) { + $extends = self::$types[$originalType]['extends']; + $extendType = $type; + if (strpos($extends, '/') !== false) { + $parts = explode('/', $extends); + $extends = array_pop($parts); + $extendType = implode('/', $parts); + } + App::uses($extends, $extendType); + if ($plugin && in_array($originalType, array('controller', 'model'))) { + App::uses($plugin . $extends, $plugin . '.' .$type); + } + } + if ($plugin) { + $plugin .= '.'; + } + $name = Inflector::camelize($name); + App::uses($name, $plugin . $type); + return class_exists($name); + } + +/** + * Helper function to include single files + * + * @param string $name unique name of the file for identifying it inside the application + * @param string $plugin camel cased plugin name if any + * @param array $search list of paths to search the file into + * @param string $file filename if known, the $name param will be used otherwise + * @param boolean $return whether this function should return the contents of the file after being parsed by php or just a success notice + * @return mixed if $return contents of the file after php parses it, boolean indicating success otherwise + */ + protected function _loadFile($name, $plugin, $search, $file, $return) { + $mapped = self::_mapped($name, $plugin); + if ($mapped) { + $file = $mapped; + } else if (!empty($search)) { + foreach ($search as $path) { + $found = false; + if (file_exists($path . $file)) { + $file = $path . $file; + $found = true; + break; + } + if (empty($found)) { + $file = false; + } + } + } + if (!empty($file) && file_exists($file)) { + self::_map($file, $name, $plugin); + $returnValue = include $file; + if ($return) { + return $returnValue; + } + return (bool) $returnValue; + } + return false; + } + +/** + * Helper function to load files from vendors folders + * + * @param string $name unique name of the file for identifying it inside the application + * @param string $plugin camel cased plugin name if any + * @param string $file file name if known + * @param string $ext file extension if known + * @return boolean true if the file was loaded successfully, false otherwise + */ + protected function _loadVendor($name, $plugin, $file, $ext) { + if ($mapped = self::_mapped($name, $plugin)) { + return (bool) include_once($mapped); + } + $fileTries = array(); + $paths = ($plugin) ? App::path('vendors', $plugin) : App::path('vendors'); + if (empty($ext)) { + $ext = 'php'; + } + if (empty($file)) { + $fileTries[] = $name . '.' . $ext; + $fileTries[] = Inflector::underscore($name) . '.' . $ext; + } else { + $fileTries[] = $file; + } + + foreach ($fileTries as $file) { + foreach ($paths as $path) { + if (file_exists($path . $file)) { + self::_map($path . $file, $name, $plugin); + return (bool) include($path . $file); + } + } + } + return false; + } + +/** + * Initializes the cache for App, registers a shutdown function. + * + * @return void + */ + public static function init() { + self::$_map += (array)Cache::read('file_map', '_cake_core_'); + self::$_objects += (array)Cache::read('object_map', '_cake_core_'); + register_shutdown_function(array('App', 'shutdown')); + self::uses('CakePlugin', 'Core'); + } + +/** + * Maps the $name to the $file. + * + * @param string $file full path to file + * @param string $name unique name for this map + * @param string $plugin camelized if object is from a plugin, the name of the plugin + * @return void + */ + protected static function _map($file, $name, $plugin = null) { + if ($plugin) { + self::$_map['Plugin'][$plugin][$name] = $file; + } else { + self::$_map[$name] = $file; + } + if (!self::$bootstrapping) { + self::$_cacheChange = true; + } + } + +/** + * Returns a file's complete path. + * + * @param string $name unique name + * @param string $plugin camelized if object is from a plugin, the name of the plugin + * @return mixed file path if found, false otherwise + */ + protected static function _mapped($name, $plugin = null) { + if ($plugin) { + if (isset(self::$_map['Plugin'][$plugin][$name])) { + return self::$_map['Plugin'][$plugin][$name]; + } + return false; + } + + if (isset(self::$_map[$name])) { + return self::$_map[$name]; + } + return false; + } + +/** + * Object destructor. + * + * Writes cache file if changes have been made to the $_map + * + * @return void + */ + public static function shutdown() { + if (self::$_cacheChange) { + Cache::write('file_map', array_filter(self::$_map), '_cake_core_'); + } + if (self::$_objectCacheChange) { + Cache::write('object_map', self::$_objects, '_cake_core_'); + } + } +} diff --git a/app/Cake/Core/CakePlugin.php b/app/Cake/Core/CakePlugin.php new file mode 100644 index 00000000..e3679a1b --- /dev/null +++ b/app/Cake/Core/CakePlugin.php @@ -0,0 +1,225 @@ + true, 'routes' => true))` will load the bootstrap.php and routes.php files + * `CakePlugin::load('DebugKit', array('bootstrap' => false, 'routes' => true))` will load routes.php file but not bootstrap.php + * `CakePlugin::load('DebugKit', array('bootstrap' => array('config1', 'config2')))` will load config1.php and config2.php files + * `CakePlugin::load('DebugKit', array('bootstrap' => 'aCallableMethod'))` will run the aCallableMethod function to initialize it + * + * Bootstrap initialization functions can be expressed as a PHP callback type, including closures. Callbacks will receive two + * parameters (plugin name, plugin configuration) + * + * It is also possible to load multiple plugins at once. Examples: + * + * `CakePlugin::load(array('DebugKit', 'ApiGenerator'))` will load the DebugKit and ApiGenerator plugins + * `CakePlugin::load(array('DebugKit', 'ApiGenerator'), array('bootstrap' => true))` will load bootstrap file for both plugins + * + * {{{ + * CakePlugin::load(array( + * 'DebugKit' => array('routes' => true), + * 'ApiGenerator' + * ), array('bootstrap' => true)) + * }}} + * + * Will only load the bootstrap for ApiGenerator and only the routes for DebugKit + * + * @param mixed $plugin name of the plugin to be loaded in CamelCase format or array or plugins to load + * @param array $config configuration options for the plugin + * @throws MissingPluginException if the folder for the plugin to be loaded is not found + * @return void + */ + public static function load($plugin, $config = array()) { + if (is_array($plugin)) { + foreach ($plugin as $name => $conf) { + list($name, $conf) = (is_numeric($name)) ? array($conf, $config) : array($name, $conf); + self::load($name, $conf); + } + return; + } + $config += array('bootstrap' => false, 'routes' => false); + if (empty($config['path'])) { + foreach (App::path('plugins') as $path) { + if (is_dir($path . $plugin)) { + self::$_plugins[$plugin] = $config + array('path' => $path . $plugin . DS); + break; + } + + //Backwards compatibility to make easier to migrate to 2.0 + $underscored = Inflector::underscore($plugin); + if (is_dir($path . $underscored)) { + self::$_plugins[$plugin] = $config + array('path' => $path . $underscored . DS); + break; + } + } + } else { + self::$_plugins[$plugin] = $config; + } + + if (empty(self::$_plugins[$plugin]['path'])) { + throw new MissingPluginException(array('plugin' => $plugin)); + } + if (!empty(self::$_plugins[$plugin]['bootstrap'])) { + self::bootstrap($plugin); + } + } + +/** + * Will load all the plugins located in the configured plugins folders + * If passed an options array, it will be used as a common default for all plugins to be loaded + * It is possible to set specific defaults for each plugins in the options array. Examples: + * + * {{{ + * CakePlugin::loadAll(array( + * array('bootstrap' => true), + * 'DebugKit' => array('routes' => true), + * )) + * }}} + * + * The above example will load the bootstrap file for all plugins, but for DebugKit it will only load the routes file + * and will not look for any bootstrap script. + * + * @param array $options + * @return void + */ + public function loadAll($options = array()) { + $plugins = App::objects('plugins'); + foreach ($plugins as $p) { + $opts = isset($options[$p]) ? $options[$p] : null; + if ($opts === null && isset($options[0])) { + $opts = $options[0]; + } + self::load($p, (array) $opts); + } + } + +/** + * Returns the filesystem path for a plugin + * + * @param string $plugin name of the plugin in CamelCase format + * @return string path to the plugin folder + * @throws MissingPluginException if the folder for plugin was not found or plugin has not been loaded + */ + public static function path($plugin) { + if (empty(self::$_plugins[$plugin])) { + throw new MissingPluginException(array('plugin' => $plugin)); + } + return self::$_plugins[$plugin]['path']; + } + +/** + * Loads the bootstrapping files for a plugin, or calls the initialization setup in the configuration + * + * @param string $plugin name of the plugin + * @return mixed + * @see CakePlugin::load() for examples of bootstrap configuration + */ + public static function bootstrap($plugin) { + $config = self::$_plugins[$plugin]; + if ($config['bootstrap'] === false) { + return false; + } + if (is_callable($config['bootstrap'])) { + return call_user_func_array($config['bootstrap'], array($plugin, $config)); + } + + $path = self::path($plugin); + if ($config['bootstrap'] === true) { + return include($path . 'Config' . DS . 'bootstrap.php'); + } + + $bootstrap = (array)$config['bootstrap']; + foreach ($bootstrap as $file) { + include $path . 'Config' . DS . $file . '.php'; + } + + return true; + } + +/** + * Loads the routes file for a plugin, or all plugins configured to load their respective routes file + * + * @param string $plugin name of the plugin, if null will operate on all plugins having enabled the + * loading of routes files + * @return boolean + */ + public static function routes($plugin = null) { + if ($plugin === null) { + foreach (self::loaded() as $p) { + self::routes($p); + } + return true; + } + $config = self::$_plugins[$plugin]; + if ($config['routes'] === false) { + return false; + } + return (bool) include self::path($plugin) . 'Config' . DS . 'routes.php'; + } + +/** + * Retruns true if the plugin $plugin is already loaded + * If plugin is null, it will return a list of all loaded plugins + * + * @param string $plugin + * @return mixed boolean true if $plugin is already loaded. + * If $plugin is null, returns a list of plugins that have been loaded + */ + public static function loaded($plugin = null) { + if ($plugin) { + return isset(self::$_plugins[$plugin]); + } + $return = array_keys(self::$_plugins); + sort($return); + return $return; + } + +/** + * Forgets a loaded plugin or all of them if first parameter is null + * + * @param string $plugin name of the plugin to forget + * @return void + */ + public static function unload($plugin = null) { + if (is_null($plugin)) { + self::$_plugins = array(); + } else { + unset(self::$_plugins[$plugin]); + } + } +} diff --git a/app/Cake/Core/Configure.php b/app/Cake/Core/Configure.php new file mode 100644 index 00000000..c91212d3 --- /dev/null +++ b/app/Cake/Core/Configure.php @@ -0,0 +1,380 @@ + 0 + ); + +/** + * Configured reader classes, used to load config files from resources + * + * @var array + * @see Configure::load() + */ + protected static $_readers = array(); + +/** + * Initializes configure and runs the bootstrap process. + * Bootstrapping includes the following steps: + * + * - Setup App array in Configure. + * - Include app/Config/core.php. + * - Configure core cache configurations. + * - Load App cache files. + * - Include app/Config/bootstrap.php. + * - Setup error/exception handlers. + * + * @param boolean $boot + * @return void + */ + public static function bootstrap($boot = true) { + if ($boot) { + self::write('App', array( + 'base' => false, + 'baseUrl' => false, + 'dir' => APP_DIR, + 'webroot' => WEBROOT_DIR, + 'www_root' => WWW_ROOT + )); + + if (!include(APP . 'Config' . DS . 'core.php')) { + trigger_error(__d('cake_dev', "Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", APP . 'Config' . DS), E_USER_ERROR); + } + App::$bootstrapping = false; + App::init(); + App::build(); + if (!include(APP . 'Config' . DS . 'bootstrap.php')) { + trigger_error(__d('cake_dev', "Can't find application bootstrap file. Please create %sbootstrap.php, and make sure it is readable by PHP.", APP . 'Config' . DS), E_USER_ERROR); + } + $level = -1; + if (isset(self::$_values['Error']['level'])) { + error_reporting(self::$_values['Error']['level']); + $level = self::$_values['Error']['level']; + } + if (!empty(self::$_values['Error']['handler'])) { + set_error_handler(self::$_values['Error']['handler'], $level); + } + if (!empty(self::$_values['Exception']['handler'])) { + set_exception_handler(self::$_values['Exception']['handler']); + } + } + } + +/** + * Used to store a dynamic variable in Configure. + * + * Usage: + * {{{ + * Configure::write('One.key1', 'value of the Configure::One[key1]'); + * Configure::write(array('One.key1' => 'value of the Configure::One[key1]')); + * Configure::write('One', array( + * 'key1' => 'value of the Configure::One[key1]', + * 'key2' => 'value of the Configure::One[key2]' + * ); + * + * Configure::write(array( + * 'One.key1' => 'value of the Configure::One[key1]', + * 'One.key2' => 'value of the Configure::One[key2]' + * )); + * }}} + * + * @link http://book.cakephp.org/view/926/write + * @param array $config Name of var to write + * @param mixed $value Value to set for var + * @return boolean True if write was successful + */ + public static function write($config, $value = null) { + if (!is_array($config)) { + $config = array($config => $value); + } + + foreach ($config as $name => $value) { + if (strpos($name, '.') === false) { + self::$_values[$name] = $value; + } else { + $names = explode('.', $name, 4); + switch (count($names)) { + case 2: + self::$_values[$names[0]][$names[1]] = $value; + break; + case 3: + self::$_values[$names[0]][$names[1]][$names[2]] = $value; + break; + case 4: + $names = explode('.', $name, 2); + if (!isset(self::$_values[$names[0]])) { + self::$_values[$names[0]] = array(); + } + self::$_values[$names[0]] = Set::insert(self::$_values[$names[0]], $names[1], $value); + break; + } + } + } + + if (isset($config['debug']) && function_exists('ini_set')) { + if (self::$_values['debug']) { + ini_set('display_errors', 1); + } else { + ini_set('display_errors', 0); + } + } + return true; + } + +/** + * Used to read information stored in Configure. Its not + * possible to store `null` values in Configure. + * + * Usage: + * {{{ + * Configure::read('Name'); will return all values for Name + * Configure::read('Name.key'); will return only the value of Configure::Name[key] + * }}} + * + * @link http://book.cakephp.org/view/927/read + * @param string $var Variable to obtain. Use '.' to access array elements. + * @return mixed value stored in configure, or null. + */ + public static function read($var = null) { + if ($var === null) { + return self::$_values; + } + if (isset(self::$_values[$var])) { + return self::$_values[$var]; + } + if (strpos($var, '.') !== false) { + $names = explode('.', $var, 3); + $var = $names[0]; + } + if (!isset(self::$_values[$var])) { + return null; + } + switch (count($names)) { + case 2: + if (isset(self::$_values[$var][$names[1]])) { + return self::$_values[$var][$names[1]]; + } + break; + case 3: + if (isset(self::$_values[$var][$names[1]][$names[2]])) { + return self::$_values[$var][$names[1]][$names[2]]; + } + if (!isset(self::$_values[$var][$names[1]])) { + return null; + } + return Set::classicExtract(self::$_values[$var][$names[1]], $names[2]); + break; + } + return null; + } + +/** + * Used to delete a variable from Configure. + * + * Usage: + * {{{ + * Configure::delete('Name'); will delete the entire Configure::Name + * Configure::delete('Name.key'); will delete only the Configure::Name[key] + * }}} + * + * @link http://book.cakephp.org/view/928/delete + * @param string $var the var to be deleted + * @return void + */ + public static function delete($var = null) { + if (strpos($var, '.') === false) { + unset(self::$_values[$var]); + return; + } + + $names = explode('.', $var, 2); + self::$_values[$names[0]] = Set::remove(self::$_values[$names[0]], $names[1]); + } + +/** + * Add a new reader to Configure. Readers allow you to read configuration + * files in various formats/storage locations. CakePHP comes with two built-in readers + * PhpReader and IniReader. You can also implement your own reader classes in your application. + * + * To add a new reader to Configure: + * + * `Configure::config('ini', new IniReader());` + * + * @param string $name The name of the reader being configured. This alias is used later to + * read values from a specific reader. + * @param ConfigReaderInterface $reader The reader to append. + * @return void + */ + public static function config($name, ConfigReaderInterface $reader) { + self::$_readers[$name] = $reader; + } + +/** + * Gets the names of the configured reader objects. + * + * @param string $name + * @return array Array of the configured reader objects. + */ + public static function configured($name = null) { + if ($name) { + return isset(self::$_readers[$name]); + } + return array_keys(self::$_readers); + } + +/** + * Remove a configured reader. This will unset the reader + * and make any future attempts to use it cause an Exception. + * + * @param string $name Name of the reader to drop. + * @return boolean Success + */ + public static function drop($name) { + if (!isset(self::$_readers[$name])) { + return false; + } + unset(self::$_readers[$name]); + return true; + } + +/** + * Loads stored configuration information from a resource. You can add + * config file resource readers with `Configure::config()`. + * + * Loaded configuration information will be merged with the current + * runtime configuration. You can load configuration files from plugins + * by preceding the filename with the plugin name. + * + * `Configure::load('Users.user', 'default')` + * + * Would load the 'user' config file using the default config reader. You can load + * app config files by giving the name of the resource you want loaded. + * + * `Configure::load('setup', 'default');` + * + * @link http://book.cakephp.org/view/929/load + * @param string $key name of configuration resource to load. + * @param string $config Name of the configured reader to use to read the resource identified by $key. + * @param boolean $merge if config files should be merged instead of simply overridden + * @return mixed false if file not found, void if load successful. + * @throws ConfigureException Will throw any exceptions the reader raises. + */ + public static function load($key, $config = 'default', $merge = true) { + if (!isset(self::$_readers[$config])) { + return false; + } + $values = self::$_readers[$config]->read($key); + + if ($merge) { + $keys = array_keys($values); + foreach ($keys as $key) { + if (($c = self::read($key)) && is_array($values[$key]) && is_array($c)) { + $values[$key] = array_merge_recursive($c, $values[$key]); + } + } + } + + return self::write($values); + } + +/** + * Used to determine the current version of CakePHP. + * + * Usage `Configure::version();` + * + * @link http://book.cakephp.org/view/930/version + * @return string Current version of CakePHP + */ + public static function version() { + if (!isset(self::$_values['Cake']['version'])) { + require CAKE . 'Config' . DS . 'config.php'; + self::write($config); + } + return self::$_values['Cake']['version']; + } + +/** + * Used to write runtime configuration into Cache. Stored runtime configuration can be + * restored using `Configure::restore()`. These methods can be used to enable configuration managers + * frontends, or other GUI type interfaces for configuration. + * + * @param string $name The storage name for the saved configuration. + * @param string $cacheConfig The cache configuration to save into. Defaults to 'default' + * @param array $data Either an array of data to store, or leave empty to store all values. + * @return boolean Success + */ + public static function store($name, $cacheConfig = 'default', $data = null) { + if ($data === null) { + $data = self::$_values; + } + return Cache::write($name, $data, $cacheConfig); + } + +/** + * Restores configuration data stored in the Cache into configure. Restored + * values will overwrite existing ones. + * + * @param string $name Name of the stored config file to load. + * @param string $cacheConfig Name of the Cache configuration to read from. + * @return boolean Success. + */ + public static function restore($name, $cacheConfig = 'default') { + $values = Cache::read($name, $cacheConfig); + if ($values) { + return self::write($values); + } + return false; + } +} + +/** + * An interface for creating objects compatible with Configure::load() + * + * @package Cake.Core + */ +interface ConfigReaderInterface { +/** + * Read method is used for reading configuration information from sources. + * These sources can either be static resources like files, or dynamic ones like + * a database, or other datasource. + * + * @param string $key + * @return array An array of data to merge into the runtime configuration + */ + public function read($key); +} diff --git a/app/Cake/Core/Object.php b/app/Cake/Core/Object.php new file mode 100644 index 00000000..bb1ee4ac --- /dev/null +++ b/app/Cake/Core/Object.php @@ -0,0 +1,197 @@ + 0, 'autoRender' => 1)); + } + if (is_array($url) && !isset($extra['url'])) { + $extra['url'] = array(); + } + $extra = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra); + + if (is_string($url)) { + $request = new CakeRequest($url); + } elseif (is_array($url)) { + $params = $url + array('pass' => array(), 'named' => array(), 'base' => false); + $params = array_merge($params, $extra); + $request = new CakeRequest(Router::reverse($params), false); + if (isset($params['data'])) { + $request->data = $params['data']; + } + } + + $dispatcher = new Dispatcher(); + $result = $dispatcher->dispatch($request, new CakeResponse(), $extra); + Router::popRequest(); + return $result; + } + +/** + * Calls a method on this object with the given parameters. Provides an OO wrapper + * for `call_user_func_array` + * + * @param string $method Name of the method to call + * @param array $params Parameter list to use when calling $method + * @return mixed Returns the result of the method call + */ + public function dispatchMethod($method, $params = array()) { + switch (count($params)) { + case 0: + return $this->{$method}(); + case 1: + return $this->{$method}($params[0]); + case 2: + return $this->{$method}($params[0], $params[1]); + case 3: + return $this->{$method}($params[0], $params[1], $params[2]); + case 4: + return $this->{$method}($params[0], $params[1], $params[2], $params[3]); + case 5: + return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]); + default: + return call_user_func_array(array(&$this, $method), $params); + break; + } + } + +/** + * Stop execution of the current script. Wraps exit() making + * testing easier. + * + * @param integer|string $status see http://php.net/exit for values + * @return void + */ + protected function _stop($status = 0) { + exit($status); + } + +/** + * Convience method to write a message to CakeLog. See CakeLog::write() + * for more information on writing to logs. + * + * @param string $msg Log message + * @param integer $type Error type constant. Defined in app/Config/core.php. + * @return boolean Success of log write + */ + public function log($msg, $type = LOG_ERROR) { + if (!class_exists('CakeLog')) { + require CAKE . 'cake_log.php'; + } + if (!is_string($msg)) { + $msg = print_r($msg, true); + } + return CakeLog::write($type, $msg); + } + +/** + * Allows setting of multiple properties of the object in a single line of code. Will only set + * properties that are part of a class declaration. + * + * @param array $properties An associative array containing properties and corresponding values. + * @return void + */ + protected function _set($properties = array()) { + if (is_array($properties) && !empty($properties)) { + $vars = get_object_vars($this); + foreach ($properties as $key => $val) { + if (array_key_exists($key, $vars)) { + $this->{$key} = $val; + } + } + } + } + +/** + * Merges this objects $property with the property in $class' definition. + * This classes value for the property will be merged on top of $class' + * + * This provides some of the DRY magic CakePHP provides. If you want to shut it off, redefine + * this method as an empty function. + * + * @param array $properties The name of the properties to merge. + * @param string $class The class to merge the property with. + * @param boolean $normalize Set to true to run the properties through Set::normalize() before merging. + * @return void + */ + protected function _mergeVars($properties, $class, $normalize = true) { + $classProperties = get_class_vars($class); + foreach ($properties as $var) { + if ( + isset($classProperties[$var]) && + !empty($classProperties[$var]) && + is_array($this->{$var}) && + $this->{$var} != $classProperties[$var] + ) { + if ($normalize) { + $classProperties[$var] = Set::normalize($classProperties[$var]); + $this->{$var} = Set::normalize($this->{$var}); + } + $this->{$var} = Set::merge($classProperties[$var], $this->{$var}); + } + } + } +} diff --git a/app/Cake/Error/ErrorHandler.php b/app/Cake/Error/ErrorHandler.php new file mode 100644 index 00000000..96f70307 --- /dev/null +++ b/app/Cake/Error/ErrorHandler.php @@ -0,0 +1,226 @@ + 1. + * + * ### Uncaught exceptions + * + * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown + * and it is a type that ErrorHandler does not know about it will be treated as a 500 error. + * + * ### Implementing application specific exception handling + * + * You can implement application specific exception handling in one of a few ways. Each approach + * gives you different amounts of control over the exception handling process. + * + * - Set Configure::write('Exception.handler', 'YourClass::yourMethod'); + * - Create AppController::appError(); + * - Set Configure::write('Exception.renderer', 'YourClass'); + * + * #### Create your own Exception handler with `Exception.handler` + * + * This gives you full control over the exception handling process. The class you choose should be + * loaded in your app/Config/bootstrap.php, so its available to handle any exceptions. You can + * define the handler as any callback type. Using Exception.handler overrides all other exception + * handling settings and logic. + * + * #### Using `AppController::appError();` + * + * This controller method is called instead of the default exception rendering. It receives the + * thrown exception as its only argument. You should implement your error handling in that method. + * Using AppController::appError(), will supersede any configuration for Exception.renderer. + * + * #### Using a custom renderer with `Exception.renderer` + * + * If you don't want to take control of the exception handling, but want to change how exceptions are + * rendered you can use `Exception.renderer` to choose a class to render exception pages. By default + * `ExceptionRenderer` is used. Your custom exception renderer class should be placed in app/Error. + * + * Your custom renderer should expect an exception in its constructor, and implement a render method. + * Failing to do so will cause additional errors. + * + * #### Logging exceptions + * + * Using the built-in exception handling, you can log all the exceptions + * that are dealt with by ErrorHandler by setting `Exception.log` to true in your core.php. + * Enabling this will log every exception to CakeLog and the configured loggers. + * + * ### PHP errors + * + * Error handler also provides the built in features for handling php errors (trigger_error). + * While in debug mode, errors will be output to the screen using debugger. While in production mode, + * errors will be logged to CakeLog. You can control which errors are logged by setting + * `Error.level` in your core.php. + * + * #### Logging errors + * + * When ErrorHandler is used for handling errors, you can enable error logging by setting `Error.log` to true. + * This will log all errors to the configured log handlers. + * + * #### Controlling what errors are logged/displayed + * + * You can control which errors are logged / displayed by ErrorHandler by setting `Error.level`. Setting this + * to one or a combination of a few of the E_* constants will only enable the specified errors. + * + * e.g. `Configure::write('Error.level', E_ALL & ~E_NOTICE);` + * + * Would enable handling for all non Notice errors. + * + * @package Cake.Error + * @see ExceptionRenderer for more information on how to customize exception rendering. + */ +class ErrorHandler { + +/** + * Set as the default exception handler by the CakePHP bootstrap process. + * + * This will either use an AppError class if your application has one, + * or use the default ExceptionRenderer. + * + * @param Exception $exception + * @return void + * @see http://php.net/manual/en/function.set-exception-handler.php + */ + public static function handleException(Exception $exception) { + $config = Configure::read('Exception'); + if (!empty($config['log'])) { + $message = sprintf("[%s] %s\n%s", + get_class($exception), + $exception->getMessage(), + $exception->getTraceAsString() + ); + CakeLog::write(LOG_ERR, $message); + } + $renderer = $config['renderer']; + if ($renderer !== 'ExceptionRenderer') { + list($plugin, $renderer) = pluginSplit($renderer, true); + App::uses($renderer, $plugin . 'Error'); + } + try { + $error = new $renderer($exception); + $error->render(); + } catch (Exception $e) { + set_error_handler(Configure::read('Error.handler')); // Should be using configured ErrorHandler + Configure::write('Error.trace', false); // trace is useless here since it's internal + $message = sprintf("[%s] %s\n%s", // Keeping same message format + get_class($e), + $e->getMessage(), + $e->getTraceAsString() + ); + trigger_error($message, E_USER_ERROR); + } + } + +/** + * Set as the default error handler by CakePHP. Use Configure::write('Error.handler', $callback), to use your own + * error handling methods. This function will use Debugger to display errors when debug > 0. And + * will log errors to CakeLog, when debug == 0. + * + * You can use Configure::write('Error.level', $value); to set what type of errors will be handled here. + * Stack traces for errors can be enabled with Configure::write('Error.trace', true); + * + * @param integer $code Code of error + * @param string $description Error description + * @param string $file File on which error occurred + * @param integer $line Line that triggered the error + * @param array $context Context + * @return boolean true if error was handled + */ + public static function handleError($code, $description, $file = null, $line = null, $context = null) { + if (error_reporting() === 0) { + return false; + } + $errorConfig = Configure::read('Error'); + list($error, $log) = self::_mapErrorCode($code); + + $debug = Configure::read('debug'); + if ($debug) { + $data = array( + 'level' => $log, + 'code' => $code, + 'error' => $error, + 'description' => $description, + 'file' => $file, + 'line' => $line, + 'context' => $context, + 'start' => 2, + 'path' => Debugger::trimPath($file) + ); + return Debugger::getInstance()->outputError($data); + } else { + $message = $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']'; + if (!empty($errorConfig['trace'])) { + $trace = Debugger::trace(array('start' => 1, 'format' => 'log')); + $message .= "\nTrace:\n" . $trace . "\n"; + } + return CakeLog::write($log, $message); + } + } + +/** + * Map an error code into an Error word, and log location. + * + * @param integer $code Error code to map + * @return array Array of error word, and log location. + */ + protected static function _mapErrorCode($code) { + $error = $log = null; + switch ($code) { + case E_PARSE: + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + $error = 'Fatal Error'; + $log = LOG_ERROR; + break; + case E_WARNING: + case E_USER_WARNING: + case E_COMPILE_WARNING: + case E_RECOVERABLE_ERROR: + $error = 'Warning'; + $log = LOG_WARNING; + break; + case E_NOTICE: + case E_USER_NOTICE: + $error = 'Notice'; + $log = LOG_NOTICE; + break; + case E_STRICT: + $error = 'Strict'; + $log = LOG_NOTICE; + break; + case E_DEPRECATED: + $error = 'Deprecated'; + $log = LOG_NOTICE; + break; + } + return array($error, $log); + } +} diff --git a/app/Cake/Error/ExceptionRenderer.php b/app/Cake/Error/ExceptionRenderer.php new file mode 100644 index 00000000..357cb9ad --- /dev/null +++ b/app/Cake/Error/ExceptionRenderer.php @@ -0,0 +1,284 @@ + 1. + * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown + * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error. + * + * ### Implementing application specific exception rendering + * + * You can implement application specific exception handling in one of a few ways: + * + * - Create a AppController::appError(); + * - Create a subclass of ExceptionRenderer and configure it to be the `Exception.renderer` + * + * #### Using AppController::appError(); + * + * This controller method is called instead of the default exception handling. It receives the + * thrown exception as its only argument. You should implement your error handling in that method. + * + * #### Using a subclass of ExceptionRenderer + * + * Using a subclass of ExceptionRenderer gives you full control over how Exceptions are rendered, you + * can configure your class in your core.php, with `Configure::write('Exception.renderer', 'MyClass');` + * You should place any custom exception renderers in `app/libs`. + * + * @package Cake.Error + */ +class ExceptionRenderer { + +/** + * Controller instance. + * + * @var Controller + */ + public $controller = null; + +/** + * template to render for CakeException + * + * @var string + */ + public $template = ''; + +/** + * The method corresponding to the Exception this object is for. + * + * @var string + */ + public $method = ''; + +/** + * The exception being handled. + * + * @var Exception + */ + public $error = null; + +/** + * Creates the controller to perform rendering on the error response. + * If the error is a CakeException it will be converted to either a 400 or a 500 + * code error depending on the code used to construct the error. + * + * @param Exception $exception Exception + */ + public function __construct(Exception $exception) { + $this->controller = $this->_getController($exception); + + if (method_exists($this->controller, 'apperror')) { + return $this->controller->appError($exception); + } + $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception))); + $code = $exception->getCode(); + + $methodExists = method_exists($this, $method); + + if ($exception instanceof CakeException && !$methodExists) { + $method = '_cakeError'; + if (empty($template)) { + $template = 'error500'; + } + if ($template == 'internalError') { + $template = 'error500'; + } + } elseif ($exception instanceof PDOException) { + $method = 'pdoError'; + $template = 'pdo_error'; + $code = 500; + } elseif (!$methodExists) { + $method = 'error500'; + if ($code >= 400 && $code < 500) { + $method = 'error400'; + } + } + + if (Configure::read('debug') == 0) { + $parentClass = get_parent_class($this); + if ($parentClass != __CLASS__) { + $method = 'error400'; + } + $parentMethods = (array)get_class_methods($parentClass); + if (in_array($method, $parentMethods)) { + $method = 'error400'; + } + if ($code == 500) { + $method = 'error500'; + } + } + $this->template = $template; + $this->method = $method; + $this->error = $exception; + } + +/** + * Get the controller instance to handle the exception. + * Override this method in subclasses to customize the controller used. + * This method returns the built in `CakeErrorController` normally, or if an error is repeated + * a bare controller will be used. + * + * @param Exception $exception The exception to get a controller for. + * @return Controller + */ + protected function _getController($exception) { + App::uses('CakeErrorController', 'Controller'); + if (!$request = Router::getRequest(false)) { + $request = new CakeRequest(); + } + $response = new CakeResponse(array('charset' => Configure::read('App.encoding'))); + try { + $controller = new CakeErrorController($request, $response); + } catch (Exception $e) { + $controller = new Controller($request, $response); + $controller->viewPath = 'Errors'; + } + return $controller; + } + +/** + * Renders the response for the exception. + * + * @return void + */ + public function render() { + if ($this->method) { + call_user_func_array(array($this, $this->method), array($this->error)); + } + } + +/** + * Generic handler for the internal framework errors CakePHP can generate. + * + * @param CakeException $error + * @return void + */ + protected function _cakeError(CakeException $error) { + $url = $this->controller->request->here(); + $code = $error->getCode(); + $this->controller->response->statusCode($code); + $this->controller->set(array( + 'code' => $code, + 'url' => h($url), + 'name' => $error->getMessage(), + 'error' => $error, + )); + try { + $this->controller->set($error->getAttributes()); + $this->_outputMessage($this->template); + } catch (Exception $e) { + $this->_outputMessageSafe('error500'); + } + } + +/** + * Convenience method to display a 400 series page. + * + * @param Exception $error + * @return void + */ + public function error400($error) { + $message = $error->getMessage(); + if (Configure::read('debug') == 0 && $error instanceof CakeException) { + $message = __d('cake', 'Not Found'); + } + $url = $this->controller->request->here(); + $this->controller->response->statusCode($error->getCode()); + $this->controller->set(array( + 'name' => $message, + 'url' => h($url), + 'error' => $error, + )); + $this->_outputMessage('error400'); + } + +/** + * Convenience method to display a 500 page. + * + * @param Exception $error + * @return void + */ + public function error500($error) { + $url = $this->controller->request->here(); + $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500; + $this->controller->response->statusCode($code); + $this->controller->set(array( + 'name' => __d('cake', 'An Internal Error Has Occurred'), + 'message' => h($url), + 'error' => $error, + )); + $this->_outputMessage('error500'); + } + +/** + * Convenience method to display a PDOException. + * + * @param PDOException $error + * @return void + */ + public function pdoError(PDOException $error) { + $url = $this->controller->request->here(); + $code = 500; + $this->controller->response->statusCode($code); + $this->controller->set(array( + 'code' => $code, + 'url' => h($url), + 'name' => $error->getMessage(), + 'error' => $error, + )); + try { + $this->_outputMessage($this->template); + } catch (Exception $e) { + $this->_outputMessageSafe('error500'); + } + } + +/** + * Generate the response using the controller object. + * + * @param string $template The template to render. + * @return void + */ + protected function _outputMessage($template) { + $this->controller->render($template); + $this->controller->afterFilter(); + $this->controller->response->send(); + } + +/** + * A safer way to render error messages, replaces all helpers, with basics + * and doesn't call component methods. + * + * @param string $template The template to render + * @return void + */ + protected function _outputMessageSafe($template) { + $this->controller->helpers = array('Form', 'Html', 'Session'); + $this->controller->render($template); + $this->controller->response->send(); + } +} diff --git a/app/Cake/Error/exceptions.php b/app/Cake/Error/exceptions.php new file mode 100644 index 00000000..65b4fb5a --- /dev/null +++ b/app/Cake/Error/exceptions.php @@ -0,0 +1,491 @@ +_attributes = $message; + $message = __d('cake_dev', $this->_messageTemplate, $message); + } + parent::__construct($message, $code); + } + +/** + * Get the passed in attributes + * + * @return array + */ + public function getAttributes() { + return $this->_attributes; + } +} + +/** + * Missing Controller exception - used when a controller + * cannot be found. + * + * @package Cake.Error + */ +class MissingControllerException extends CakeException { + protected $_messageTemplate = 'Controller class %s could not be found.'; + + public function __construct($message, $code = 404) { + parent::__construct($message, $code); + } +} + +/** + * Missing Action exception - used when a controller action + * cannot be found. + * + * @package Cake.Error + */ +class MissingActionException extends CakeException { + protected $_messageTemplate = 'Action %s::%s() could not be found.'; + + public function __construct($message, $code = 404) { + parent::__construct($message, $code); + } +} +/** + * Private Action exception - used when a controller action + * starts with a `_`. + * + * @package Cake.Error + */ +class PrivateActionException extends CakeException { + protected $_messageTemplate = 'Private Action %s::%s() is not directly accessible.'; + + public function __construct($message, $code = 404, Exception $previous = null) { + parent::__construct($message, $code, $previous); + } +} + +/** + * Used when a Component file cannot be found. + * + * @package Cake.Error + */ +class MissingComponentFileException extends CakeException { + protected $_messageTemplate = 'Component file "%s" is missing.'; +} + +/** + * Used when a Component class cannot be found. + * + * @package Cake.Error + */ +class MissingComponentClassException extends CakeException { + protected $_messageTemplate = 'Component class "%s" is missing.'; +} + +/** + * Used when a Behavior file cannot be found. + * + * @package Cake.Error + */ +class MissingBehaviorFileException extends CakeException { } + +/** + * Used when a Behavior class cannot be found. + * + * @package Cake.Error + */ +class MissingBehaviorClassException extends CakeException { } + +/** + * Used when a view file cannot be found. + * + * @package Cake.Error + */ +class MissingViewException extends CakeException { + protected $_messageTemplate = 'View file "%s" is missing.'; +} + +/** + * Used when a layout file cannot be found. + * + * @package Cake.Error + */ +class MissingLayoutException extends CakeException { + protected $_messageTemplate = 'Layout file "%s" is missing.'; +} + +/** + * Used when a helper file cannot be found. + * + * @package Cake.Error + */ +class MissingHelperFileException extends CakeException { + protected $_messageTemplate = 'Helper file "%s" is missing.'; +} + +/** + * Used when a helper class cannot be found. + * + * @package Cake.Error + */ +class MissingHelperClassException extends CakeException { + protected $_messageTemplate = 'Helper class "%s" is missing.'; +} + + +/** + * Runtime Exceptions for ConnectionManager + * + * @package Cake.Error + */ +class MissingDatabaseException extends CakeException { + protected $_messageTemplate = 'Database connection "%s" could not be found.'; +} + +/** + * Used when no connections can be found. + * + * @package Cake.Error + */ +class MissingConnectionException extends CakeException { + protected $_messageTemplate = 'Database connection "%s" is missing, or could not be created.'; +} + +/** + * Used when a Task file cannot be found. + * + * @package Cake.Error + */ +class MissingTaskFileException extends CakeException { + protected $_messageTemplate = 'Task file "%s" is missing.'; +} + +/** + * Used when a Task class cannot be found. + * + * @package Cake.Error + */ +class MissingTaskClassException extends CakeException { + protected $_messageTemplate = 'Task class "%s" is missing.'; +} + +/** + * Used when a shell method cannot be found. + * + * @package Cake.Error + */ +class MissingShellMethodException extends CakeException { + protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`"; +} + +/** + * Used when a shell class cannot be found. + * + * @package Cake.Error + */ +class MissingShellClassException extends CakeException { + protected $_messageTemplate = "Shell class %s could not be loaded."; +} + +/** + * Used when a shell file cannot be found. + * + * @package Cake.Error + */ +class MissingShellFileException extends CakeException { + protected $_messageTemplate = "Shell file %s could not be loaded."; +} + +/** + * Exception class to be thrown when a datasource configuration is not found + * + * @package Cake.Error + */ +class MissingDatasourceConfigException extends CakeException { + protected $_messageTemplate = 'The datasource configuration "%s" was not found in database.php'; +} + +/** + * Exception class to be thrown when a datasource is not found + * + * @package Cake.Error + */ +class MissingDatasourceFileException extends CakeException { + protected $_messageTemplate = 'Datasource "%s" was not found.'; +} + +/** + * Exception class to be thrown when a database table is not found in the datasource + * + * @package Cake.Error + */ +class MissingTableException extends CakeException { + protected $_messageTemplate = 'Database table %s for model %s was not found.'; +} + +/** + * Exception raised when a Model could not be found. + * + * @package Cake.Error + */ +class MissingModelException extends CakeException { + protected $_messageTemplate = 'Model %s could not be found.'; +} + +/** + * Exception raised when a test loader could not be found + * + * @package Cake.Error + */ +class MissingTestLoaderException extends CakeException { + protected $_messageTemplate = 'Test loader %s could not be found.'; +} + +/** + * Exception raised when a plugin could not be found + * + * @package Cake.Error + */ +class MissingPluginException extends CakeException { + protected $_messageTemplate = 'Plugin %s could not be found.'; +} + +/** + * Exception class for Cache. This exception will be thrown from Cache when it + * encounters an error. + * + * @package Cake.Error + */ +class CacheException extends CakeException { } + +/** + * Exception class for Router. This exception will be thrown from Router when it + * encounters an error. + * + * @package Cake.Error + */ +class RouterException extends CakeException { } + +/** + * Exception class for CakeLog. This exception will be thrown from CakeLog when it + * encounters an error. + * + * @package Cake.Error + */ +class CakeLogException extends CakeException { } + +/** + * Exception class for CakeSession. This exception will be thrown from CakeSession when it + * encounters an error. + * + * @package Cake.Error + */ +class CakeSessionException extends CakeException { } + +/** + * Exception class for Configure. This exception will be thrown from Configure when it + * encounters an error. + * + * @package Cake.Error + */ +class ConfigureException extends CakeException { } + +/** + * Exception class for Socket. This exception will be thrown from CakeSocket, CakeEmail, HttpSocket + * SmtpTransport and HttpResponse when it encounters an error. + * + * @package Cake.Error + */ +class SocketException extends CakeException { } + +/** + * Exception class for Xml. This exception will be thrown from Xml when it + * encounters an error. + * + * @package Cake.Error + */ +class XmlException extends CakeException { } + +/** + * Exception class for Console libraries. This exception will be thrown from Console library + * classes when they encounter an error. + * + * @package Cake.Error + */ +class ConsoleException extends CakeException { } diff --git a/app/Cake/I18n/I18n.php b/app/Cake/I18n/I18n.php new file mode 100644 index 00000000..f5783849 --- /dev/null +++ b/app/Cake/I18n/I18n.php @@ -0,0 +1,570 @@ +l10n = new L10n(); + } + return $instance[0]; + } + +/** + * Used by the translation functions in basics.php + * Returns a translated string based on current language and translation files stored in locale folder + * + * @param string $singular String to translate + * @param string $plural Plural string (if any) + * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations + * @param string $category Category The integer value of the category to use. + * @param integer $count Count Count is used with $plural to choose the correct plural form. + * @return string translated string. + */ + public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null) { + $_this = I18n::getInstance(); + + if (strpos($singular, "\r\n") !== false) { + $singular = str_replace("\r\n", "\n", $singular); + } + if ($plural !== null && strpos($plural, "\r\n") !== false) { + $plural = str_replace("\r\n", "\n", $plural); + } + + if (is_numeric($category)) { + $_this->category = $_this->_categories[$category]; + } + $language = Configure::read('Config.language'); + + if (!empty($_SESSION['Config']['language'])) { + $language = $_SESSION['Config']['language']; + } + + if (($_this->_lang && $_this->_lang !== $language) || !$_this->_lang) { + $lang = $_this->l10n->get($language); + $_this->_lang = $lang; + } + + if (is_null($domain)) { + $domain = 'default'; + } + + $_this->domain = $domain . '_' . $_this->l10n->lang; + + if (!isset($_this->_domains[$domain][$_this->_lang])) { + $_this->_domains[$domain][$_this->_lang] = Cache::read($_this->domain, '_cake_core_'); + } + + if (!isset($_this->_domains[$domain][$_this->_lang][$_this->category])) { + $_this->_bindTextDomain($domain); + Cache::write($_this->domain, $_this->_domains[$domain][$_this->_lang], '_cake_core_'); + } + + if ($_this->category == 'LC_TIME') { + return $_this->_translateTime($singular,$domain); + } + + if (!isset($count)) { + $plurals = 0; + } elseif (!empty($_this->_domains[$domain][$_this->_lang][$_this->category]["%plural-c"]) && $_this->_noLocale === false) { + $header = $_this->_domains[$domain][$_this->_lang][$_this->category]["%plural-c"]; + $plurals = $_this->_pluralGuess($header, $count); + } else { + if ($count != 1) { + $plurals = 1; + } else { + $plurals = 0; + } + } + + if (!empty($_this->_domains[$domain][$_this->_lang][$_this->category][$singular])) { + if (($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular]) || ($plurals) && ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$plural])) { + if (is_array($trans)) { + if (isset($trans[$plurals])) { + $trans = $trans[$plurals]; + } + } + if (strlen($trans)) { + return $trans; + } + } + } + + if (!empty($plurals)) { + return $plural; + } + return $singular; + } + +/** + * Clears the domains internal data array. Useful for testing i18n. + * + * @return void + */ + public static function clear() { + $self = I18n::getInstance(); + $self->_domains = array(); + } + +/** + * Get the loaded domains cache. + * + * @return array + */ + public static function domains() { + $self = I18n::getInstance(); + return $self->_domains; + } + +/** + * Attempts to find the plural form of a string. + * + * @param string $header Type + * @param integer $n Number + * @return integer plural match + */ + protected function _pluralGuess($header, $n) { + if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) { + return 0; + } + + if ($header === "nplurals=2;plural=n!=1;") { + return $n != 1 ? 1 : 0; + } elseif ($header === "nplurals=2;plural=n>1;") { + return $n > 1 ? 1 : 0; + } + + if (strpos($header, "plurals=3")) { + if (strpos($header, "100!=11")) { + if (strpos($header, "10<=4")) { + return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); + } elseif (strpos($header, "100<10")) { + return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); + } + return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2); + } elseif (strpos($header, "n==2")) { + return $n == 1 ? 0 : ($n == 2 ? 1 : 2); + } elseif (strpos($header, "n==0")) { + return $n == 1 ? 0 : ($n == 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2); + } elseif (strpos($header, "n>=2")) { + return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2); + } elseif (strpos($header, "10>=2")) { + return $n == 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); + } + return $n % 10 == 1 ? 0 : ($n % 10 == 2 ? 1 : 2); + } elseif (strpos($header, "plurals=4")) { + if (strpos($header, "100==2")) { + return $n % 100 == 1 ? 0 : ($n % 100 == 2 ? 1 : ($n % 100 == 3 || $n % 100 == 4 ? 2 : 3)); + } elseif (strpos($header, "n>=3")) { + return $n == 1 ? 0 : ($n == 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3)); + } elseif (strpos($header, "100>=1")) { + return $n == 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3)); + } + } elseif (strpos($header, "plurals=5")) { + return $n == 1 ? 0 : ($n == 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4))); + } + } + +/** + * Binds the given domain to a file in the specified directory. + * + * @param string $domain Domain to bind + * @return string Domain binded + */ + protected function _bindTextDomain($domain) { + $this->_noLocale = true; + $core = true; + $merge = array(); + $searchPaths = App::path('locales'); + $plugins = CakePlugin::loaded(); + + if (!empty($plugins)) { + foreach ($plugins as $plugin) { + $pluginDomain = Inflector::underscore($plugin); + if ($pluginDomain === $domain) { + $searchPaths[] = CakePlugin::path($plugin) . 'Locale' . DS; + $searchPaths = array_reverse($searchPaths); + break; + } + } + } + + + foreach ($searchPaths as $directory) { + + foreach ($this->l10n->languagePath as $lang) { + $file = $directory . $lang . DS . $this->category . DS . $domain; + $localeDef = $directory . $lang . DS . $this->category; + + if ($core) { + $app = $directory . $lang . DS . $this->category . DS . 'core'; + + if (file_exists($fn = "$app.mo")) { + $this->_loadMo($fn, $domain); + $this->_noLocale = false; + $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category]; + $core = null; + } elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) { + $this->_loadPo($f, $domain); + $this->_noLocale = false; + $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category]; + $core = null; + } + } + + if (file_exists($fn = "$file.mo")) { + $this->_loadMo($fn, $domain); + $this->_noLocale = false; + break 2; + } elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) { + $this->_loadPo($f, $domain); + $this->_noLocale = false; + break 2; + } elseif (is_file($localeDef) && ($f = fopen($localeDef, "r"))) { + $this->_loadLocaleDefinition($f, $domain); + $this->_noLocale = false; + return $domain; + } + } + } + + if (empty($this->_domains[$domain][$this->_lang][$this->category])) { + $this->_domains[$domain][$this->_lang][$this->category] = array(); + return $domain; + } + + if (isset($this->_domains[$domain][$this->_lang][$this->category][""])) { + $head = $this->_domains[$domain][$this->_lang][$this->category][""]; + + foreach (explode("\n", $head) as $line) { + $header = strtok($line,":"); + $line = trim(strtok("\n")); + $this->_domains[$domain][$this->_lang][$this->category]["%po-header"][strtolower($header)] = $line; + } + + if (isset($this->_domains[$domain][$this->_lang][$this->category]["%po-header"]["plural-forms"])) { + $switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->_domains[$domain][$this->_lang][$this->category]["%po-header"]["plural-forms"]); + $this->_domains[$domain][$this->_lang][$this->category]["%plural-c"] = $switch; + unset($this->_domains[$domain][$this->_lang][$this->category]["%po-header"]); + } + $this->_domains = Set::pushDiff($this->_domains, $merge); + + if (isset($this->_domains[$domain][$this->_lang][$this->category][null])) { + unset($this->_domains[$domain][$this->_lang][$this->category][null]); + } + } + return $domain; + } + +/** + * Loads the binary .mo file for translation and sets the values for this translation in the var I18n::_domains + * + * @param resource $file Binary .mo file to load + * @param string $domain Domain where to load file in + * @return void + */ + protected function _loadMo($file, $domain) { + $data = file_get_contents($file); + + if ($data) { + $header = substr($data, 0, 20); + $header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header); + extract($header); + + if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && $version == 0) { + for ($n = 0; $n < $count; $n++) { + $r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8)); + $msgid = substr($data, $r["offs"], $r["len"]); + unset($msgid_plural); + + if (strpos($msgid, "\000")) { + list($msgid, $msgid_plural) = explode("\000", $msgid); + } + $r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8)); + $msgstr = substr($data, $r["offs"], $r["len"]); + + if (strpos($msgstr, "\000")) { + $msgstr = explode("\000", $msgstr); + } + $this->_domains[$domain][$this->_lang][$this->category][$msgid] = $msgstr; + + if (isset($msgid_plural)) { + $this->_domains[$domain][$this->_lang][$this->category][$msgid_plural] =& $this->_domains[$domain][$this->_lang][$this->category][$msgid]; + } + } + } + } + } + +/** + * Loads the text .po file for translation and sets the values for this translation in the var I18n::_domains + * + * @param resource $file Text .po file to load + * @param string $domain Domain to load file in + * @return array Binded domain elements + */ + protected function _loadPo($file, $domain) { + $type = 0; + $translations = array(); + $translationKey = ""; + $plural = 0; + $header = ""; + + do { + $line = trim(fgets($file)); + if ($line == "" || $line[0] == "#") { + continue; + } + if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) { + $type = 1; + $translationKey = stripcslashes($regs[1]); + } elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) { + $type = 2; + $translationKey = ""; + } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) { + $type = 3; + $translationKey .= stripcslashes($regs[1]); + } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) { + $translations[$translationKey] = stripcslashes($regs[1]); + $type = 4; + } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) { + $type = 4; + $translations[$translationKey] = ""; + } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) { + $translations[$translationKey] .= stripcslashes($regs[1]); + } elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) { + $type = 6; + } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) { + $type = 6; + } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) { + $plural = $regs[1]; + $translations[$translationKey][$plural] = stripcslashes($regs[2]); + $type = 7; + } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) { + $plural = $regs[1]; + $translations[$translationKey][$plural] = ""; + $type = 7; + } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) { + $translations[$translationKey][$plural] .= stripcslashes($regs[1]); + } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) { + $header .= stripcslashes($regs[1]); + $type = 5; + } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) { + $header = ""; + $type = 5; + } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) { + $header .= stripcslashes($regs[1]); + } else { + unset($translations[$translationKey]); + $type = 0; + $translationKey = ""; + $plural = 0; + } + } while (!feof($file)); + fclose($file); + $merge[""] = $header; + return $this->_domains[$domain][$this->_lang][$this->category] = array_merge($merge ,$translations); + } + +/** + * Parses a locale definition file following the POSIX standard + * + * @param resource $file file handler + * @param string $domain Domain where locale definitions will be stored + * @return void + */ + protected function _loadLocaleDefinition($file, $domain = null) { + $comment = '#'; + $escape = '\\'; + $currentToken = false; + $value = ''; + while ($line = fgets($file)) { + $line = trim($line); + if (empty($line) || $line[0] === $comment) { + continue; + } + $parts = preg_split("/[[:space:]]+/",$line); + if ($parts[0] === 'comment_char') { + $comment = $parts[1]; + continue; + } + if ($parts[0] === 'escape_char') { + $escape = $parts[1]; + continue; + } + $count = count($parts); + if ($count == 2) { + $currentToken = $parts[0]; + $value = $parts[1]; + } elseif ($count == 1) { + $value .= $parts[0]; + } else { + continue; + } + + $len = strlen($value) - 1; + if ($value[$len] === $escape) { + $value = substr($value, 0, $len); + continue; + } + + $mustEscape = array($escape . ',' , $escape . ';', $escape . '<', $escape . '>', $escape . $escape); + $replacements = array_map('crc32', $mustEscape); + $value = str_replace($mustEscape, $replacements, $value); + $value = explode(';', $value); + $this->__escape = $escape; + foreach ($value as $i => $val) { + $val = trim($val, '"'); + $val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$this, '_parseLiteralValue'), $val); + $val = str_replace($replacements, $mustEscape, $val); + $value[$i] = $val; + } + if (count($value) == 1) { + $this->_domains[$domain][$this->_lang][$this->category][$currentToken] = array_pop($value); + } else { + $this->_domains[$domain][$this->_lang][$this->category][$currentToken] = $value; + } + } + } + +/** + * Auxiliary function to parse a symbol from a locale definition file + * + * @param string $string Symbol to be parsed + * @return string parsed symbol + */ + protected function _parseLiteralValue($string) { + $string = $string[1]; + if (substr($string, 0, 2) === $this->__escape . 'x') { + $delimiter = $this->__escape . 'x'; + return join('', array_map('chr', array_map('hexdec',array_filter(explode($delimiter, $string))))); + } + if (substr($string, 0, 2) === $this->__escape . 'd') { + $delimiter = $this->__escape . 'd'; + return join('', array_map('chr', array_filter(explode($delimiter, $string)))); + } + if ($string[0] === $this->__escape && isset($string[1]) && is_numeric($string[1])) { + $delimiter = $this->__escape; + return join('', array_map('chr', array_filter(explode($delimiter, $string)))); + } + if (substr($string, 0, 3) === 'U00') { + $delimiter = 'U00'; + return join('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string))))); + } + if (preg_match('/U([0-9a-fA-F]{4})/', $string, $match)) { + return Multibyte::ascii(array(hexdec($match[1]))); + } + return $string; + } + +/** + * Returns a Time format definition from corresponding domain + * + * @param string $format Format to be translated + * @param string $domain Domain where format is stored + * @return mixed translated format string if only value or array of translated strings for corresponding format. + */ + protected function _translateTime($format, $domain) { + if (!empty($this->_domains[$domain][$this->_lang]['LC_TIME'][$format])) { + if (($trans = $this->_domains[$domain][$this->_lang][$this->category][$format])) { + return $trans; + } + } + return $format; + } +} diff --git a/app/Cake/I18n/L10n.php b/app/Cake/I18n/L10n.php new file mode 100644 index 00000000..9c973b0a --- /dev/null +++ b/app/Cake/I18n/L10n.php @@ -0,0 +1,472 @@ + 'af', + /* Albanian */ 'alb' => 'sq', + /* Arabic */ 'ara' => 'ar', + /* Armenian - Armenia */ 'hye' => 'hy', + /* Basque */ 'baq' => 'eu', + /* Tibetan */ 'bod' => 'bo', + /* Bosnian */ 'bos' => 'bs', + /* Bulgarian */ 'bul' => 'bg', + /* Byelorussian */ 'bel' => 'be', + /* Catalan */ 'cat' => 'ca', + /* Chinese */ 'chi' => 'zh', + /* Chinese */ 'zho' => 'zh', + /* Croatian */ 'hrv' => 'hr', + /* Czech */ 'cze' => 'cs', + /* Czech */ 'ces' => 'cs', + /* Danish */ 'dan' => 'da', + /* Dutch (Standard) */ 'dut' => 'nl', + /* Dutch (Standard) */ 'nld' => 'nl', + /* English */ 'eng' => 'en', + /* Estonian */ 'est' => 'et', + /* Faeroese */ 'fao' => 'fo', + /* Farsi */ 'fas' => 'fa', + /* Farsi */ 'per' => 'fa', + /* Finnish */ 'fin' => 'fi', + /* French (Standard) */ 'fre' => 'fr', + /* French (Standard) */ 'fra' => 'fr', + /* Gaelic (Scots) */ 'gla' => 'gd', + /* Galician */ 'glg' => 'gl', + /* German (Standard) */ 'deu' => 'de', + /* German (Standard) */ 'ger' => 'de', + /* Greek */ 'gre' => 'el', + /* Greek */ 'ell' => 'el', + /* Hebrew */ 'heb' => 'he', + /* Hindi */ 'hin' => 'hi', + /* Hungarian */ 'hun' => 'hu', + /* Icelandic */ 'ice' => 'is', + /* Icelandic */ 'isl' => 'is', + /* Indonesian */ 'ind' => 'id', + /* Irish */ 'gle' => 'ga', + /* Italian */ 'ita' => 'it', + /* Japanese */ 'jpn' => 'ja', + /* Korean */ 'kor' => 'ko', + /* Latvian */ 'lav' => 'lv', + /* Lithuanian */ 'lit' => 'lt', + /* Macedonian */ 'mac' => 'mk', + /* Macedonian */ 'mkd' => 'mk', + /* Malaysian */ 'may' => 'ms', + /* Malaysian */ 'msa' => 'ms', + /* Maltese */ 'mlt' => 'mt', + /* Norwegian */ 'nor' => 'no', + /* Norwegian Bokmal */ 'nob' => 'nb', + /* Norwegian Nynorsk */ 'nno' => 'nn', + /* Polish */ 'pol' => 'pl', + /* Portuguese (Portugal) */ 'por' => 'pt', + /* Rhaeto-Romanic */ 'roh' => 'rm', + /* Romanian */ 'rum' => 'ro', + /* Romanian */ 'ron' => 'ro', + /* Russian */ 'rus' => 'ru', + /* Sami (Lappish) */ 'smi' => 'sz', + /* Serbian */ 'scc' => 'sr', + /* Serbian */ 'srp' => 'sr', + /* Slovak */ 'slo' => 'sk', + /* Slovak */ 'slk' => 'sk', + /* Slovenian */ 'slv' => 'sl', + /* Sorbian */ 'wen' => 'sb', + /* Spanish (Spain - Traditional) */ 'spa' => 'es', + /* Swedish */ 'swe' => 'sv', + /* Thai */ 'tha' => 'th', + /* Tsonga */ 'tso' => 'ts', + /* Tswana */ 'tsn' => 'tn', + /* Turkish */ 'tur' => 'tr', + /* Ukrainian */ 'ukr' => 'uk', + /* Urdu */ 'urd' => 'ur', + /* Venda */ 'ven' => 've', + /* Vietnamese */ 'vie' => 'vi', + /* Welsh */ 'cym' => 'cy', + /* Xhosa */ 'xho' => 'xh', + /* Yiddish */ 'yid' => 'yi', + /* Zulu */ 'zul' => 'zu'); + +/** + * HTTP_ACCEPT_LANGUAGE catalog + * + * holds all information related to a language + * + * @var array + */ + protected $_l10nCatalog = array('af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'bo' => array('language' => 'Tibetan', 'locale' => 'bod', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'bo-cn' => array('language' => 'Tibetan (China)', 'locale' => 'bo_cn', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'bo-in' => array('language' => 'Tibetan (India)', 'locale' => 'bo_in', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8', 'direction' => 'rtl'), + 'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'), + 'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'), + 'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'), + 'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r', 'direction' => 'ltr'), + 'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sv-fi' => array('language' => 'Swedish (Finland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8', 'direction' => 'rtl'), + 've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'cy' => array('language' => 'Welsh', 'locale' => 'cym', 'localeFallback' => 'cym', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312', 'direction' => 'ltr'), + 'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8', 'direction' => 'ltr')); + +/** + * Class constructor + */ + public function __construct() { + if (defined('DEFAULT_LANGUAGE')) { + $this->default = DEFAULT_LANGUAGE; + } + } + +/** + * Gets the settings for $language. + * If $language is null it attempt to get settings from L10n::_autoLanguage(); if this fails + * the method will get the settings from L10n::_setLanguage(); + * + * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined) + * @return mixed + */ + public function get($language = null) { + if ($language !== null) { + return $this->_setLanguage($language); + } elseif ($this->_autoLanguage() === false) { + return $this->_setLanguage(); + } + } + +/** + * Sets the class vars to correct values for $language. + * If $language is null it will use the DEFAULT_LANGUAGE if defined + * + * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined) + * @return mixed + */ + protected function _setLanguage($language = null) { + $langKey = null; + if ($language !== null && isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { + $langKey = $this->_l10nMap[$language]; + } else if ($language !== null && isset($this->_l10nCatalog[$language])) { + $langKey = $language; + } else if (defined('DEFAULT_LANGUAGE')) { + $langKey = $language = DEFAULT_LANGUAGE; + } + + if ($langKey !== null && isset($this->_l10nCatalog[$langKey])) { + $this->language = $this->_l10nCatalog[$langKey]['language']; + $this->languagePath = array( + $this->_l10nCatalog[$langKey]['locale'], + $this->_l10nCatalog[$langKey]['localeFallback'] + ); + $this->lang = $language; + $this->locale = $this->_l10nCatalog[$langKey]['locale']; + $this->charset = $this->_l10nCatalog[$langKey]['charset']; + $this->direction = $this->_l10nCatalog[$langKey]['direction']; + } else { + $this->lang = $language; + $this->languagePath = array($language); + } + + if ($this->default) { + if (isset($this->_l10nMap[$this->default]) && isset($this->_l10nCatalog[$this->_l10nMap[$this->default]])) { + $this->languagePath[] = $this->_l10nCatalog[$this->_l10nMap[$this->default]]['localeFallback']; + } else if (isset($this->_l10nCatalog[$this->default])) { + $this->languagePath[] = $this->_l10nCatalog[$this->default]['localeFallback']; + } + } + $this->found = true; + + if (Configure::read('Config.language') === null) { + Configure::write('Config.language', $this->lang); + } + + if ($language) { + return $language; + } + } + +/** + * Attempts to find the locale settings based on the HTTP_ACCEPT_LANGUAGE variable + * + * @return boolean Success + */ + protected function _autoLanguage() { + $_detectableLanguages = CakeRequest::acceptLanguage(); + foreach ($_detectableLanguages as $key => $langKey) { + if (isset($this->_l10nCatalog[$langKey])) { + $this->_setLanguage($langKey); + return true; + } else if (strpos($langKey, '-') !== false) { + $langKey = substr($langKey, 0, 2); + if (isset($this->_l10nCatalog[$langKey])) { + $this->_setLanguage($langKey); + return true; + } + } + } + return false; + } + +/** + * Attempts to find locale for language, or language for locale + * + * @param mixed $mixed 2/3 char string (language/locale), array of those strings, or null + * @return mixed string language/locale, array of those values, whole map as an array, + * or false when language/locale doesn't exist + */ + public function map($mixed = null) { + if (is_array($mixed)) { + $result = array(); + foreach ($mixed as $_mixed) { + if ($_result = $this->map($_mixed)) { + $result[$_mixed] = $_result; + } + } + return $result; + } else if (is_string($mixed)) { + if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) { + return array_search($mixed, $this->_l10nMap); + } else if (isset($this->_l10nMap[$mixed])) { + return $this->_l10nMap[$mixed]; + } + return false; + } + return $this->_l10nMap; + } + +/** + * Attempts to find catalog record for requested language + * + * @param mixed $language string requested language, array of requested languages, or null for whole catalog + * @return mixed array catalog record for requested language, array of catalog records, whole catalog, + * or false when language doesn't exist + */ + public function catalog($language = null) { + if (is_array($language)) { + $result = array(); + foreach ($language as $_language) { + if ($_result = $this->catalog($_language)) { + $result[$_language] = $_result; + } + } + return $result; + } else if (is_string($language)) { + if (isset($this->_l10nCatalog[$language])) { + return $this->_l10nCatalog[$language]; + } else if (isset($this->_l10nMap[$language]) && isset($this->_l10nCatalog[$this->_l10nMap[$language]])) { + return $this->_l10nCatalog[$this->_l10nMap[$language]]; + } + return false; + } + return $this->_l10nCatalog; + } +} diff --git a/app/Cake/I18n/Multibyte.php b/app/Cake/I18n/Multibyte.php new file mode 100644 index 00000000..bc581b50 --- /dev/null +++ b/app/Cake/I18n/Multibyte.php @@ -0,0 +1,1106 @@ + 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + } + } + + if (!$found && isset($haystack[$position])) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } + +/** + * Finds the last occurrence of a character in a string within another, case insensitive. + * + * @param string $haystack The string from which to get the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. + * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, + * Default value is false. + * @return string|boolean The portion of $haystack. or false if $needle is not found. + */ + public static function strrichr($haystack, $needle, $part = false) { + $check = Multibyte::strtoupper($haystack); + $check = Multibyte::utf8($check); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($check); + + $needle = Multibyte::strtoupper($needle); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + if ($needle[$i] === $check[($position + $i) -1]) { + $found = true; + } + unset($parts[$position - 1]); + $haystack = array_merge(array($haystack[$position]), $haystack); + break; + } + } + if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + } + } + + if (!$found && isset($haystack[$position])) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } + +/** + * Finds position of last occurrence of a string within another, case insensitive + * + * @param string $haystack The string from which to get the position of the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset The position in $haystack to start searching. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string, + * or false if $needle is not found. + */ + public static function strripos($haystack, $needle, $offset = 0) { + if (Multibyte::checkMultibyte($haystack)) { + $found = false; + $haystack = Multibyte::strtoupper($haystack); + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($haystack); + + $needle = Multibyte::strtoupper($needle); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $position = $offset; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $haystack[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $haystack[$position + $i]) { + if ($needle[$i] === $haystack[($position + $i) -1]) { + $position--; + $found = true; + continue; + } + } + } + + if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + $position--; + } + } + $position++; + } + return ($found) ? $position : false; + } + return strripos($haystack, $needle, $offset); + } + +/** + * Find position of last occurrence of a string in a string. + * + * @param string $haystack The string being checked, for the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string. + * Negative values will stop searching at an arbitrary point prior to the end of the string. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string. + * If $needle is not found, it returns false. + */ + public static function strrpos($haystack, $needle, $offset = 0) { + if (Multibyte::checkMultibyte($haystack)) { + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($haystack); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $position = $offset; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $haystack[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $haystack[$position + $i]) { + if ($needle[$i] === $haystack[($position + $i) -1]) { + $position--; + $found = true; + continue; + } + } + } + + if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + $position--; + } + } + $position++; + } + return ($found) ? $position : false; + } + return strrpos($haystack, $needle, $offset); + } + +/** + * Finds first occurrence of a string within another + * + * @param string $haystack The string from which to get the first occurrence of $needle. + * @param string $needle The string to find in $haystack + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. + * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, + * Default value is FALSE. + * @return string|boolean The portion of $haystack, or true if $needle is not found. + */ + public static function strstr($haystack, $needle, $part = false) { + $php = (PHP_VERSION < 5.3); + + if (($php && $part) || Multibyte::checkMultibyte($haystack)) { + $check = Multibyte::utf8($haystack); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + break; + } + } + if ($i === $needleCount) { + $found = true; + } + } + if (!$found) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } + + if (!$php) { + return strstr($haystack, $needle, $part); + } + return strstr($haystack, $needle); + } + +/** + * Make a string lowercase + * + * @param string $string The string being lowercased. + * @return string with all alphabetic characters converted to lowercase. + */ + public static function strtolower($string) { + $utf8Map = Multibyte::utf8($string); + + $length = count($utf8Map); + $lowerCase = array(); + + for ($i = 0 ; $i < $length; $i++) { + $char = $utf8Map[$i]; + + if ($char < 128) { + $str = strtolower(chr($char)); + $strlen = strlen($str); + for ($ii = 0 ; $ii < $strlen; $ii++) { + $lower = ord(substr($str, $ii, 1)); + } + $lowerCase[] = $lower; + $matched = true; + } else { + $matched = false; + $keys = self::_find($char, 'upper'); + + if (!empty($keys)) { + foreach ($keys as $key => $value) { + if ($keys[$key]['upper'] == $char && count($keys[$key]['lower'][0]) === 1) { + $lowerCase[] = $keys[$key]['lower'][0]; + $matched = true; + break 1; + } + } + } + } + if ($matched === false) { + $lowerCase[] = $char; + } + } + return Multibyte::ascii($lowerCase); + } + +/** + * Make a string uppercase + * + * @param string $string The string being uppercased. + * @return string with all alphabetic characters converted to uppercase. + */ + public static function strtoupper($string) { + $utf8Map = Multibyte::utf8($string); + + $length = count($utf8Map); + $replaced = array(); + $upperCase = array(); + + for ($i = 0 ; $i < $length; $i++) { + $char = $utf8Map[$i]; + + if ($char < 128) { + $str = strtoupper(chr($char)); + $strlen = strlen($str); + for ($ii = 0 ; $ii < $strlen; $ii++) { + $upper = ord(substr($str, $ii, 1)); + } + $upperCase[] = $upper; + $matched = true; + + } else { + $matched = false; + $keys = self::_find($char); + $keyCount = count($keys); + + if (!empty($keys)) { + foreach ($keys as $key => $value) { + $matched = false; + $replace = 0; + if ($length > 1 && count($keys[$key]['lower']) > 1) { + $j = 0; + + for ($ii = 0, $count = count($keys[$key]['lower']); $ii < $count; $ii++) { + $nextChar = $utf8Map[$i + $ii]; + + if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) { + $replace++; + } + } + if ($replace == $count) { + $upperCase[] = $keys[$key]['upper']; + $replaced = array_merge($replaced, array_values($keys[$key]['lower'])); + $matched = true; + break 1; + } + } elseif ($length > 1 && $keyCount > 1) { + $j = 0; + for ($ii = 1; $ii < $keyCount; $ii++) { + $nextChar = $utf8Map[$i + $ii - 1]; + + if (in_array($nextChar, $keys[$ii]['lower'])) { + + for ($jj = 0, $count = count($keys[$ii]['lower']); $jj < $count; $jj++) { + $nextChar = $utf8Map[$i + $jj]; + + if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) { + $replace++; + } + } + if ($replace == $count) { + $upperCase[] = $keys[$ii]['upper']; + $replaced = array_merge($replaced, array_values($keys[$ii]['lower'])); + $matched = true; + break 2; + } + } + } + } + if ($keys[$key]['lower'][0] == $char) { + $upperCase[] = $keys[$key]['upper']; + $matched = true; + break 1; + } + } + } + } + if ($matched === false && !in_array($char, $replaced, true)) { + $upperCase[] = $char; + } + } + return Multibyte::ascii($upperCase); + } + +/** + * Count the number of substring occurrences + * + * @param string $haystack The string being checked. + * @param string $needle The string being found. + * @return integer The number of times the $needle substring occurs in the $haystack string. + */ + public static function substrCount($haystack, $needle) { + $count = 0; + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + $matches = array_count_values($haystack); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + if ($needleCount === 1 && isset($matches[$needle[0]])) { + return $matches[$needle[0]]; + } + + for ($i = 0; $i < $haystackCount; $i++) { + if (isset($needle[0]) && $needle[0] === $haystack[$i]) { + for ($ii = 1; $ii < $needleCount; $ii++) { + if ($needle[$ii] === $haystack[$i + 1]) { + if ((isset($needle[$ii + 1]) && $haystack[$i + 2]) && $needle[$ii + 1] !== $haystack[$i + 2]) { + $count--; + } else { + $count++; + } + } + } + } + } + return $count; + } + +/** + * Get part of string + * + * @param string $string The string being checked. + * @param integer $start The first position used in $string. + * @param integer $length The maximum length of the returned string. + * @return string The portion of $string specified by the $string and $length parameters. + */ + public static function substr($string, $start, $length = null) { + if ($start === 0 && $length === null) { + return $string; + } + + $string = Multibyte::utf8($string); + + for ($i = 1; $i <= $start; $i++) { + unset($string[$i - 1]); + } + + if ($length === null || count($string) < $length) { + return Multibyte::ascii($string); + } + $string = array_values($string); + + $value = array(); + for ($i = 0; $i < $length; $i++) { + $value[] = $string[$i]; + } + return Multibyte::ascii($value); + } + +/** + * Prepare a string for mail transport, using the provided encoding + * + * @param string $string value to encode + * @param string $charset charset to use for encoding. defaults to UTF-8 + * @param string $newline + * @return string + * @TODO: add support for 'Q'('Quoted Printable') encoding + */ + public static function mimeEncode($string, $charset = null, $newline = "\r\n") { + if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) { + return $string; + } + + if (empty($charset)) { + $charset = Configure::read('App.encoding'); + } + $charset = strtoupper($charset); + + $start = '=?' . $charset . '?B?'; + $end = '?='; + $spacer = $end . $newline . ' ' . $start; + + $length = 75 - strlen($start) - strlen($end); + $length = $length - ($length % 4); + if ($charset == 'UTF-8') { + $parts = array(); + $maxchars = floor(($length * 3) / 4); + while (strlen($string) > $maxchars) { + $i = $maxchars; + $test = ord($string[$i]); + while ($test >= 128 && $test <= 191) { + $i--; + $test = ord($string[$i]); + } + $parts[] = base64_encode(substr($string, 0, $i)); + $string = substr($string, $i); + } + $parts[] = base64_encode($string); + $string = implode($spacer, $parts); + } else { + $string = chunk_split(base64_encode($string), $length, $spacer); + $string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string); + } + return $start . $string . $end; + } + +/** + * Return the Code points range for Unicode characters + * + * @param integer $decimal + * @return string + */ + protected static function _codepoint($decimal) { + if ($decimal > 128 && $decimal < 256) { + $return = '0080_00ff'; // Latin-1 Supplement + } elseif ($decimal < 384) { + $return = '0100_017f'; // Latin Extended-A + } elseif ($decimal < 592) { + $return = '0180_024F'; // Latin Extended-B + } elseif ($decimal < 688) { + $return = '0250_02af'; // IPA Extensions + } elseif ($decimal >= 880 && $decimal < 1024) { + $return = '0370_03ff'; // Greek and Coptic + } elseif ($decimal < 1280) { + $return = '0400_04ff'; // Cyrillic + } elseif ($decimal < 1328) { + $return = '0500_052f'; // Cyrillic Supplement + } elseif ($decimal < 1424) { + $return = '0530_058f'; // Armenian + } elseif ($decimal >= 7680 && $decimal < 7936) { + $return = '1e00_1eff'; // Latin Extended Additional + } elseif ($decimal < 8192) { + $return = '1f00_1fff'; // Greek Extended + } elseif ($decimal >= 8448 && $decimal < 8528) { + $return = '2100_214f'; // Letterlike Symbols + } elseif ($decimal < 8592) { + $return = '2150_218f'; // Number Forms + } elseif ($decimal >= 9312 && $decimal < 9472) { + $return = '2460_24ff'; // Enclosed Alphanumerics + } elseif ($decimal >= 11264 && $decimal < 11360) { + $return = '2c00_2c5f'; // Glagolitic + } elseif ($decimal < 11392) { + $return = '2c60_2c7f'; // Latin Extended-C + } elseif ($decimal < 11520) { + $return = '2c80_2cff'; // Coptic + } elseif ($decimal >= 65280 && $decimal < 65520) { + $return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms + } else { + $return = false; + } + self::$_codeRange[$decimal] = $return; + return $return; + } + +/** + * Find the related code folding values for $char + * + * @param integer $char decimal value of character + * @param string $type + * @return array + */ + protected static function _find($char, $type = 'lower') { + $found = array(); + if (!isset(self::$_codeRange[$char])) { + $range = self::_codepoint($char); + if ($range === false) { + return null; + } + if (!Configure::configured('_cake_core_')) { + App::uses('PhpReader', 'Configure'); + Configure::config('_cake_core_', new PhpReader(CAKE . 'Config' . DS)); + } + Configure::load('unicode' . DS . 'casefolding' . DS . $range, '_cake_core_'); + self::$_caseFold[$range] = Configure::read($range); + Configure::delete($range); + } + + if (!self::$_codeRange[$char]) { + return null; + } + self::$_table = self::$_codeRange[$char]; + $count = count(self::$_caseFold[self::$_table]); + + for ($i = 0; $i < $count; $i++) { + if ($type === 'lower' && self::$_caseFold[self::$_table][$i][$type][0] === $char) { + $found[] = self::$_caseFold[self::$_table][$i]; + } elseif ($type === 'upper' && self::$_caseFold[self::$_table][$i][$type] === $char) { + $found[] = self::$_caseFold[self::$_table][$i]; + } + } + return $found; + } + +/** + * Check the $string for multibyte characters + * @param string $string value to test + * @return boolean + */ + public static function checkMultibyte($string) { + $length = strlen($string); + + for ($i = 0; $i < $length; $i++ ) { + $value = ord(($string[$i])); + if ($value > 128) { + return true; + } + } + return false; + } +} diff --git a/app/Cake/LICENSE.txt b/app/Cake/LICENSE.txt new file mode 100644 index 00000000..59976a60 --- /dev/null +++ b/app/Cake/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License + +CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org) +Copyright 2005-2011, Cake Software Foundation, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/app/Cake/Log/CakeLog.php b/app/Cake/Log/CakeLog.php new file mode 100644 index 00000000..042f05d2 --- /dev/null +++ b/app/Cake/Log/CakeLog.php @@ -0,0 +1,208 @@ + 'FileLog'));` + * + * See the documentation on CakeLog::config() for more detail. + * + * ### Writing to the log + * + * You write to the logs using CakeLog::write(). See its documentation for more information. + * + * @package Cake.Log + */ +class CakeLog { + +/** + * An array of connected streams. + * Each stream represents a callable that will be called when write() is called. + * + * @var array + */ + protected static $_streams = array(); + +/** + * Configure and add a new logging stream to CakeLog + * You can use add loggers from app/Log/Engine use app.loggername, or any plugin/Log/Engine using plugin.loggername. + * + * ### Usage: + * + * {{{ + * CakeLog::config('second_file', array( + * 'engine' => 'FileLog', + * 'path' => '/var/logs/my_app/' + * )); + * }}} + * + * Will configure a FileLog instance to use the specified path. All options that are not `engine` + * are passed onto the logging adapter, and handled there. Any class can be configured as a logging + * adapter as long as it implements the methods in CakeLogInterface. + * + * @param string $key The keyname for this logger, used to remove the logger later. + * @param array $config Array of configuration information for the logger + * @return boolean success of configuration. + * @throws CakeLogException + */ + public static function config($key, $config) { + if (empty($config['engine'])) { + throw new CakeLogException(__d('cake_dev', 'Missing logger classname')); + } + $loggerName = $config['engine']; + unset($config['engine']); + $className = self::_getLogger($loggerName); + $logger = new $className($config); + if (!$logger instanceof CakeLogInterface) { + throw new CakeLogException(sprintf( + __d('cake_dev', 'logger class %s does not implement a write method.'), $loggerName + )); + } + self::$_streams[$key] = $logger; + return true; + } + +/** + * Attempts to import a logger class from the various paths it could be on. + * Checks that the logger class implements a write method as well. + * + * @param string $loggerName the plugin.className of the logger class you want to build. + * @return mixed boolean false on any failures, string of classname to use if search was successful. + * @throws CakeLogException + */ + protected static function _getLogger($loggerName) { + list($plugin, $loggerName) = pluginSplit($loggerName, true); + + App::uses($loggerName, $plugin . 'Log/Engine'); + if (!class_exists($loggerName)) { + throw new CakeLogException(__d('cake_dev', 'Could not load class %s', $loggerName)); + } + return $loggerName; + } + +/** + * Returns the keynames of the currently active streams + * + * @return array Array of configured log streams. + */ + public static function configured() { + return array_keys(self::$_streams); + } + +/** + * Removes a stream from the active streams. Once a stream has been removed + * it will no longer have messages sent to it. + * + * @param string $streamName Key name of a configured stream to remove. + * @return void + */ + public static function drop($streamName) { + unset(self::$_streams[$streamName]); + } + +/** + * Configures the automatic/default stream a FileLog. + * + * @return void + */ + protected static function _autoConfig() { + self::_getLogger('FileLog'); + self::$_streams['default'] = new FileLog(array('path' => LOGS)); + } + +/** + * Writes the given message and type to all of the configured log adapters. + * Configured adapters are passed both the $type and $message variables. $type + * is one of the following strings/values. + * + * ### Types: + * + * - `LOG_WARNING` => 'warning', + * - `LOG_NOTICE` => 'notice', + * - `LOG_INFO` => 'info', + * - `LOG_DEBUG` => 'debug', + * - `LOG_ERR` => 'error', + * - `LOG_ERROR` => 'error' + * + * ### Usage: + * + * Write a message to the 'warning' log: + * + * `CakeLog::write('warning', 'Stuff is broken here');` + * + * @param string $type Type of message being written + * @param string $message Message content to log + * @return boolean Success + */ + public static function write($type, $message) { + if (!defined('LOG_ERROR')) { + define('LOG_ERROR', 2); + } + if (!defined('LOG_ERR')) { + define('LOG_ERR', LOG_ERROR); + } + $levels = array( + LOG_WARNING => 'warning', + LOG_NOTICE => 'notice', + LOG_INFO => 'info', + LOG_DEBUG => 'debug', + LOG_ERR => 'error', + LOG_ERROR => 'error' + ); + + if (is_int($type) && isset($levels[$type])) { + $type = $levels[$type]; + } + if (empty(self::$_streams)) { + self::_autoConfig(); + } + foreach (self::$_streams as $logger) { + $logger->write($type, $message); + } + return true; + } +} diff --git a/app/Cake/Log/CakeLogInterface.php b/app/Cake/Log/CakeLogInterface.php new file mode 100644 index 00000000..9f6f29cd --- /dev/null +++ b/app/Cake/Log/CakeLogInterface.php @@ -0,0 +1,35 @@ + LOGS); + $this->_path = $options['path']; + } + +/** + * Implements writing to log files. + * + * @param string $type The type of log you are making. + * @param string $message The message you want to log. + * @return boolean success of write. + */ + public function write($type, $message) { + $debugTypes = array('notice', 'info', 'debug'); + + if ($type == 'error' || $type == 'warning') { + $filename = $this->_path . 'error.log'; + } elseif (in_array($type, $debugTypes)) { + $filename = $this->_path . 'debug.log'; + } else { + $filename = $this->_path . $type . '.log'; + } + $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n"; + return file_put_contents($filename, $output, FILE_APPEND); + } +} diff --git a/app/Cake/Model/AclNode.php b/app/Cake/Model/AclNode.php new file mode 100644 index 00000000..742dd250 --- /dev/null +++ b/app/Cake/Model/AclNode.php @@ -0,0 +1,184 @@ + array('nested')); + +/** + * Constructor + * + */ + public function __construct() { + $config = Configure::read('Acl.database'); + if (isset($config)) { + $this->useDbConfig = $config; + } + parent::__construct(); + } + +/** + * Retrieves the Aro/Aco node for this model + * + * @param mixed $ref Array with 'model' and 'foreign_key', model object, or string value + * @return array Node found in database + */ + public function node($ref = null) { + $db = $this->getDataSource(); + $type = $this->alias; + $result = null; + + if (!empty($this->useTable)) { + $table = $this->useTable; + } else { + $table = Inflector::pluralize(Inflector::underscore($type)); + } + + if (empty($ref)) { + return null; + } elseif (is_string($ref)) { + $path = explode('/', $ref); + $start = $path[0]; + unset($path[0]); + + $queryData = array( + 'conditions' => array( + $db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"), + $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght")), + 'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'), + 'joins' => array(array( + 'table' => $db->fullTableName($this), + 'alias' => "{$type}0", + 'type' => 'LEFT', + 'conditions' => array("{$type}0.alias" => $start) + )), + 'order' => $db->name("{$type}.lft") . ' DESC' + ); + + foreach ($path as $i => $alias) { + $j = $i - 1; + + $queryData['joins'][] = array( + 'table' => $db->fullTableName($this), + 'alias' => "{$type}{$i}", + 'type' => 'LEFT', + 'conditions' => array( + $db->name("{$type}{$i}.lft") . ' > ' . $db->name("{$type}{$j}.lft"), + $db->name("{$type}{$i}.rght") . ' < ' . $db->name("{$type}{$j}.rght"), + $db->name("{$type}{$i}.alias") . ' = ' . $db->value($alias, 'string'), + $db->name("{$type}{$j}.id") . ' = ' . $db->name("{$type}{$i}.parent_id") + ) + ); + + $queryData['conditions'] = array('or' => array( + $db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght"), + $db->name("{$type}.lft") . ' <= ' . $db->name("{$type}{$i}.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}{$i}.rght")) + ); + } + $result = $db->read($this, $queryData, -1); + $path = array_values($path); + + if ( + !isset($result[0][$type]) || + (!empty($path) && $result[0][$type]['alias'] != $path[count($path) - 1]) || + (empty($path) && $result[0][$type]['alias'] != $start) + ) { + return false; + } + } elseif (is_object($ref) && is_a($ref, 'Model')) { + $ref = array('model' => $ref->alias, 'foreign_key' => $ref->id); + } elseif (is_array($ref) && !(isset($ref['model']) && isset($ref['foreign_key']))) { + $name = key($ref); + + $model = ClassRegistry::init(array('class' => $name, 'alias' => $name)); + + if (empty($model)) { + trigger_error(__d('cake_dev', "Model class '%s' not found in AclNode::node() when trying to bind %s object", $type, $this->alias), E_USER_WARNING); + return null; + } + + $tmpRef = null; + if (method_exists($model, 'bindNode')) { + $tmpRef = $model->bindNode($ref); + } + if (empty($tmpRef)) { + $ref = array('model' => $name, 'foreign_key' => $ref[$name][$model->primaryKey]); + } else { + if (is_string($tmpRef)) { + return $this->node($tmpRef); + } + $ref = $tmpRef; + } + } + if (is_array($ref)) { + if (is_array(current($ref)) && is_string(key($ref))) { + $name = key($ref); + $ref = current($ref); + } + foreach ($ref as $key => $val) { + if (strpos($key, $type) !== 0 && strpos($key, '.') === false) { + unset($ref[$key]); + $ref["{$type}0.{$key}"] = $val; + } + } + $queryData = array( + 'conditions' => $ref, + 'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'), + 'joins' => array(array( + 'table' => $db->fullTableName($this), + 'alias' => "{$type}0", + 'type' => 'LEFT', + 'conditions' => array( + $db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"), + $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght") + ) + )), + 'order' => $db->name("{$type}.lft") . ' DESC' + ); + $result = $db->read($this, $queryData, -1); + + if (!$result) { + trigger_error(__d('cake_dev', "AclNode::node() - Couldn't find %s node identified by \"%s\"", $type, print_r($ref, true)), E_USER_WARNING); + } + } + return $result; + } +} diff --git a/app/Cake/Model/Aco.php b/app/Cake/Model/Aco.php new file mode 100644 index 00000000..488f3045 --- /dev/null +++ b/app/Cake/Model/Aco.php @@ -0,0 +1,46 @@ + array('with' => 'Permission')); +} \ No newline at end of file diff --git a/app/Cake/Model/AcoAction.php b/app/Cake/Model/AcoAction.php new file mode 100644 index 00000000..83357475 --- /dev/null +++ b/app/Cake/Model/AcoAction.php @@ -0,0 +1,46 @@ + array('with' => 'Permission')); +} diff --git a/app/Cake/Model/Behavior/AclBehavior.php b/app/Cake/Model/Behavior/AclBehavior.php new file mode 100644 index 00000000..d6c8446c --- /dev/null +++ b/app/Cake/Model/Behavior/AclBehavior.php @@ -0,0 +1,137 @@ + 'Aro', 'controlled' => 'Aco', 'both' => array('Aro', 'Aco')); + +/** + * Sets up the configuation for the model, and loads ACL models if they haven't been already + * + * @param Model $model + * @param array $config + * @return void + */ + public function setup($model, $config = array()) { + if (is_string($config)) { + $config = array('type' => $config); + } + $this->settings[$model->name] = array_merge(array('type' => 'controlled'), (array)$config); + $this->settings[$model->name]['type'] = strtolower($this->settings[$model->name]['type']); + + $types = $this->_typeMaps[$this->settings[$model->name]['type']]; + + if (!is_array($types)) { + $types = array($types); + } + foreach ($types as $type) { + $model->{$type} = ClassRegistry::init($type); + } + if (!method_exists($model, 'parentNode')) { + trigger_error(__d('cake_dev', 'Callback parentNode() not defined in %s', $model->alias), E_USER_WARNING); + } + } + +/** + * Retrieves the Aro/Aco node for this model + * + * @param Model $model + * @param mixed $ref + * @param string $type Only needed when Acl is set up as 'both', specify 'Aro' or 'Aco' to get the correct node + * @return array + * @link http://book.cakephp.org/view/1322/node + */ + public function node($model, $ref = null, $type = null) { + if (empty($type)) { + $type = $this->_typeMaps[$this->settings[$model->name]['type']]; + if (is_array($type)) { + trigger_error(__d('cake_dev', 'AclBehavior is setup with more then one type, please specify type parameter for node()'), E_USER_WARNING); + return null; + } + } + if (empty($ref)) { + $ref = array('model' => $model->name, 'foreign_key' => $model->id); + } + return $model->{$type}->node($ref); + } + +/** + * Creates a new ARO/ACO node bound to this record + * + * @param Model $model + * @param boolean $created True if this is a new record + * @return void + */ + public function afterSave($model, $created) { + $types = $this->_typeMaps[$this->settings[$model->name]['type']]; + if (!is_array($types)) { + $types = array($types); + } + foreach ($types as $type) { + $parent = $model->parentNode(); + if (!empty($parent)) { + $parent = $this->node($model, $parent, $type); + } + $data = array( + 'parent_id' => isset($parent[0][$type]['id']) ? $parent[0][$type]['id'] : null, + 'model' => $model->name, + 'foreign_key' => $model->id + ); + if (!$created) { + $node = $this->node($model, null, $type); + $data['id'] = isset($node[0][$type]['id']) ? $node[0][$type]['id'] : null; + } + $model->{$type}->create(); + $model->{$type}->save($data); + } + } + +/** + * Destroys the ARO/ACO node bound to the deleted record + * + * @param Model $model + * @return void + */ + public function afterDelete($model) { + $types = $this->_typeMaps[$this->settings[$model->name]['type']]; + if (!is_array($types)) { + $types = array($types); + } + foreach ($types as $type) { + $node = Set::extract($this->node($model, null, $type), "0.{$type}.id"); + if (!empty($node)) { + $model->{$type}->delete($node); + } + } + } +} diff --git a/app/Cake/Model/Behavior/ContainableBehavior.php b/app/Cake/Model/Behavior/ContainableBehavior.php new file mode 100644 index 00000000..54bb5475 --- /dev/null +++ b/app/Cake/Model/Behavior/ContainableBehavior.php @@ -0,0 +1,450 @@ +settings[$Model->alias])) { + $this->settings[$Model->alias] = array('recursive' => true, 'notices' => true, 'autoFields' => true); + } + if (!is_array($settings)) { + $settings = array(); + } + $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings); + } + +/** + * Runs before a find() operation. Used to allow 'contain' setting + * as part of the find call, like this: + * + * `Model->find('all', array('contain' => array('Model1', 'Model2')));` + * + * {{{ + * Model->find('all', array('contain' => array( + * 'Model1' => array('Model11', 'Model12'), + * 'Model2', + * 'Model3' => array( + * 'Model31' => 'Model311', + * 'Model32', + * 'Model33' => array('Model331', 'Model332') + * ))); + * }}} + * + * @param Model $Model Model using the behavior + * @param array $query Query parameters as set by cake + * @return array + */ + public function beforeFind($Model, $query) { + $reset = (isset($query['reset']) ? $query['reset'] : true); + $noContain = ( + (isset($this->runtime[$Model->alias]['contain']) && empty($this->runtime[$Model->alias]['contain'])) || + (isset($query['contain']) && empty($query['contain'])) + ); + $contain = array(); + if (isset($this->runtime[$Model->alias]['contain'])) { + $contain = $this->runtime[$Model->alias]['contain']; + unset($this->runtime[$Model->alias]['contain']); + } + if (isset($query['contain'])) { + $contain = array_merge($contain, (array)$query['contain']); + } + if ( + $noContain || !$contain || in_array($contain, array(null, false), true) || + (isset($contain[0]) && $contain[0] === null) + ) { + if ($noContain) { + $query['recursive'] = -1; + } + return $query; + } + if ((isset($contain[0]) && is_bool($contain[0])) || is_bool(end($contain))) { + $reset = is_bool(end($contain)) + ? array_pop($contain) + : array_shift($contain); + } + $containments = $this->containments($Model, $contain); + $map = $this->containmentsMap($containments); + + $mandatory = array(); + foreach ($containments['models'] as $name => $model) { + $instance = $model['instance']; + $needed = $this->fieldDependencies($instance, $map, false); + if (!empty($needed)) { + $mandatory = array_merge($mandatory, $needed); + } + if ($contain) { + $backupBindings = array(); + foreach ($this->types as $relation) { + if (!empty($instance->__backAssociation[$relation])) { + $backupBindings[$relation] = $instance->__backAssociation[$relation]; + } else { + $backupBindings[$relation] = $instance->{$relation}; + } + } + foreach ($this->types as $type) { + $unbind = array(); + foreach ($instance->{$type} as $assoc => $options) { + if (!isset($model['keep'][$assoc])) { + $unbind[] = $assoc; + } + } + if (!empty($unbind)) { + if (!$reset && empty($instance->__backOriginalAssociation)) { + $instance->__backOriginalAssociation = $backupBindings; + } else if ($reset && empty($instance->__backContainableAssociation)) { + $instance->__backContainableAssociation = $backupBindings; + } + $instance->unbindModel(array($type => $unbind), $reset); + } + foreach ($instance->{$type} as $assoc => $options) { + if (isset($model['keep'][$assoc]) && !empty($model['keep'][$assoc])) { + if (isset($model['keep'][$assoc]['fields'])) { + $model['keep'][$assoc]['fields'] = $this->fieldDependencies($containments['models'][$assoc]['instance'], $map, $model['keep'][$assoc]['fields']); + } + if (!$reset && empty($instance->__backOriginalAssociation)) { + $instance->__backOriginalAssociation = $backupBindings; + } else if ($reset) { + $instance->__backAssociation[$type] = $backupBindings[$type]; + } + $instance->{$type}[$assoc] = array_merge($instance->{$type}[$assoc], $model['keep'][$assoc]); + } + if (!$reset) { + $instance->__backInnerAssociation[] = $assoc; + } + } + } + } + } + + if ($this->settings[$Model->alias]['recursive']) { + $query['recursive'] = (isset($query['recursive'])) ? $query['recursive'] : $containments['depth']; + } + + $autoFields = ($this->settings[$Model->alias]['autoFields'] + && !in_array($Model->findQueryType, array('list', 'count')) + && !empty($query['fields'])); + + if (!$autoFields) { + return $query; + } + + $query['fields'] = (array)$query['fields']; + foreach (array('hasOne', 'belongsTo') as $type) { + if (!empty($Model->{$type})) { + foreach ($Model->{$type} as $assoc => $data) { + if ($Model->useDbConfig == $Model->{$assoc}->useDbConfig && !empty($data['fields'])) { + foreach ((array) $data['fields'] as $field) { + $query['fields'][] = (strpos($field, '.') === false ? $assoc . '.' : '') . $field; + } + } + } + } + } + + if (!empty($mandatory[$Model->alias])) { + foreach ($mandatory[$Model->alias] as $field) { + if ($field == '--primaryKey--') { + $field = $Model->primaryKey; + } else if (preg_match('/^.+\.\-\-[^-]+\-\-$/', $field)) { + list($modelName, $field) = explode('.', $field); + if ($Model->useDbConfig == $Model->{$modelName}->useDbConfig) { + $field = $modelName . '.' . ( + ($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field + ); + } else { + $field = null; + } + } + if ($field !== null) { + $query['fields'][] = $field; + } + } + } + $query['fields'] = array_unique($query['fields']); + return $query; + } + +/** + * Resets original associations on models that may have receive multiple, + * subsequent unbindings. + * + * @param Model $Model Model on which we are resetting + * @param array $results Results of the find operation + * @param boolean $primary true if this is the primary model that issued the find operation, false otherwise + * @return void + */ + public function afterFind($Model, $results, $primary) { + if (!empty($Model->__backContainableAssociation)) { + foreach ($Model->__backContainableAssociation as $relation => $bindings) { + $Model->{$relation} = $bindings; + unset($Model->__backContainableAssociation); + } + } + } + +/** + * Unbinds all relations from a model except the specified ones. Calling this function without + * parameters unbinds all related models. + * + * @param Model $Model Model on which binding restriction is being applied + * @return void + * @link http://book.cakephp.org/view/1323/Containable#Using-Containable-1324 + */ + public function contain($Model) { + $args = func_get_args(); + $contain = call_user_func_array('am', array_slice($args, 1)); + $this->runtime[$Model->alias]['contain'] = $contain; + } + +/** + * Permanently restore the original binding settings of given model, useful + * for restoring the bindings after using 'reset' => false as part of the + * contain call. + * + * @param Model $Model Model on which to reset bindings + * @return void + */ + public function resetBindings($Model) { + if (!empty($Model->__backOriginalAssociation)) { + $Model->__backAssociation = $Model->__backOriginalAssociation; + unset($Model->__backOriginalAssociation); + } + $Model->resetAssociations(); + if (!empty($Model->__backInnerAssociation)) { + $assocs = $Model->__backInnerAssociation; + $Model->__backInnerAssociation = array(); + foreach ($assocs as $currentModel) { + $this->resetBindings($Model->$currentModel); + } + } + } + +/** + * Process containments for model. + * + * @param Model $Model Model on which binding restriction is being applied + * @param array $contain Parameters to use for restricting this model + * @param array $containments Current set of containments + * @param boolean $throwErrors Wether unexisting bindings show throw errors + * @return array Containments + */ + public function containments($Model, $contain, $containments = array(), $throwErrors = null) { + $options = array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery'); + $keep = array(); + $depth = array(); + if ($throwErrors === null) { + $throwErrors = (empty($this->settings[$Model->alias]) ? true : $this->settings[$Model->alias]['notices']); + } + foreach ((array)$contain as $name => $children) { + if (is_numeric($name)) { + $name = $children; + $children = array(); + } + if (preg_match('/(? $children); + } + + $children = (array)$children; + foreach ($children as $key => $val) { + if (is_string($key) && is_string($val) && !in_array($key, $options, true)) { + $children[$key] = (array) $val; + } + } + + $keys = array_keys($children); + if ($keys && isset($children[0])) { + $keys = array_merge(array_values($children), $keys); + } + + foreach ($keys as $i => $key) { + if (is_array($key)) { + continue; + } + $optionKey = in_array($key, $options, true); + if (!$optionKey && is_string($key) && preg_match('/^[a-z(]/', $key) && (!isset($Model->{$key}) || !is_object($Model->{$key}))) { + $option = 'fields'; + $val = array($key); + if ($key{0} == '(') { + $val = preg_split('/\s*,\s*/', substr(substr($key, 1), 0, -1)); + } elseif (preg_match('/ASC|DESC$/', $key)) { + $option = 'order'; + $val = $Model->{$name}->alias.'.'.$key; + } elseif (preg_match('/[ =!]/', $key)) { + $option = 'conditions'; + $val = $Model->{$name}->alias.'.'.$key; + } + $children[$option] = is_array($val) ? $val : array($val); + $newChildren = null; + if (!empty($name) && !empty($children[$key])) { + $newChildren = $children[$key]; + } + unset($children[$key], $children[$i]); + $key = $option; + $optionKey = true; + if (!empty($newChildren)) { + $children = Set::merge($children, $newChildren); + } + } + if ($optionKey && isset($children[$key])) { + if (!empty($keep[$name][$key]) && is_array($keep[$name][$key])) { + $keep[$name][$key] = array_merge((isset($keep[$name][$key]) ? $keep[$name][$key] : array()), (array) $children[$key]); + } else { + $keep[$name][$key] = $children[$key]; + } + unset($children[$key]); + } + } + + if (!isset($Model->{$name}) || !is_object($Model->{$name})) { + if ($throwErrors) { + trigger_error(__d('cake_dev', 'Model "%s" is not associated with model "%s"', $Model->alias, $name), E_USER_WARNING); + } + continue; + } + + $containments = $this->containments($Model->{$name}, $children, $containments); + $depths[] = $containments['depth'] + 1; + if (!isset($keep[$name])) { + $keep[$name] = array(); + } + } + + if (!isset($containments['models'][$Model->alias])) { + $containments['models'][$Model->alias] = array('keep' => array(),'instance' => &$Model); + } + + $containments['models'][$Model->alias]['keep'] = array_merge($containments['models'][$Model->alias]['keep'], $keep); + $containments['depth'] = empty($depths) ? 0 : max($depths); + return $containments; + } + +/** + * Calculate needed fields to fetch the required bindings for the given model. + * + * @param Model $Model Model + * @param array $map Map of relations for given model + * @param mixed $fields If array, fields to initially load, if false use $Model as primary model + * @return array Fields + */ + public function fieldDependencies($Model, $map, $fields = array()) { + if ($fields === false) { + foreach ($map as $parent => $children) { + foreach ($children as $type => $bindings) { + foreach ($bindings as $dependency) { + if ($type == 'hasAndBelongsToMany') { + $fields[$parent][] = '--primaryKey--'; + } else if ($type == 'belongsTo') { + $fields[$parent][] = $dependency . '.--primaryKey--'; + } + } + } + } + return $fields; + } + if (empty($map[$Model->alias])) { + return $fields; + } + foreach ($map[$Model->alias] as $type => $bindings) { + foreach ($bindings as $dependency) { + $innerFields = array(); + switch ($type) { + case 'belongsTo': + $fields[] = $Model->{$type}[$dependency]['foreignKey']; + break; + case 'hasOne': + case 'hasMany': + $innerFields[] = $Model->$dependency->primaryKey; + $fields[] = $Model->primaryKey; + break; + } + if (!empty($innerFields) && !empty($Model->{$type}[$dependency]['fields'])) { + $Model->{$type}[$dependency]['fields'] = array_unique(array_merge($Model->{$type}[$dependency]['fields'], $innerFields)); + } + } + } + return array_unique($fields); + } + +/** + * Build the map of containments + * + * @param array $containments Containments + * @return array Built containments + */ + public function containmentsMap($containments) { + $map = array(); + foreach ($containments['models'] as $name => $model) { + $instance = $model['instance']; + foreach ($this->types as $type) { + foreach ($instance->{$type} as $assoc => $options) { + if (isset($model['keep'][$assoc])) { + $map[$name][$type] = isset($map[$name][$type]) ? array_merge($map[$name][$type], (array)$assoc) : (array)$assoc; + } + } + } + } + return $map; + } +} diff --git a/app/Cake/Model/Behavior/TranslateBehavior.php b/app/Cake/Model/Behavior/TranslateBehavior.php new file mode 100644 index 00000000..b7193cf4 --- /dev/null +++ b/app/Cake/Model/Behavior/TranslateBehavior.php @@ -0,0 +1,530 @@ + array('field_one', + * 'field_two' => 'FieldAssoc', 'field_three')) + * + * With above example only one permanent hasMany will be joined (for field_two + * as FieldAssoc) + * + * $config could be empty - and translations configured dynamically by + * bindTranslation() method + * + * @param Model $model Model the behavior is being attached to. + * @param array $config Array of configuration information. + * @return mixed + */ + public function setup($model, $config = array()) { + $db = ConnectionManager::getDataSource($model->useDbConfig); + if (!$db->connected) { + trigger_error( + __d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $model->useDbConfig, $model->alias), + E_USER_ERROR + ); + return false; + } + + $this->settings[$model->alias] = array(); + $this->runtime[$model->alias] = array('fields' => array()); + $this->translateModel($model); + return $this->bindTranslation($model, $config, false); + } + +/** + * Cleanup Callback unbinds bound translations and deletes setting information. + * + * @param Model $model Model being detached. + * @return void + */ + public function cleanup($model) { + $this->unbindTranslation($model); + unset($this->settings[$model->alias]); + unset($this->runtime[$model->alias]); + } + +/** + * beforeFind Callback + * + * @param Model $model Model find is being run on. + * @param array $query Array of Query parameters. + * @return array Modified query + */ + public function beforeFind($model, $query) { + $this->runtime[$model->alias]['virtualFields'] = $model->virtualFields; + $locale = $this->_getLocale($model); + if (empty($locale)) { + return $query; + } + $db = $model->getDataSource(); + $RuntimeModel = $this->translateModel($model); + if (!empty($RuntimeModel->tablePrefix)) { + $tablePrefix = $RuntimeModel->tablePrefix; + } else { + $tablePrefix = $db->config['prefix']; + } + + if ($tablePrefix == $db->config['prefix']) { + $tablePrefix = null; + } + + if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) { + $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count'; + $query['joins'][] = array( + 'type' => 'INNER', + 'alias' => $RuntimeModel->alias, + 'table' => $db->fullTableName($tablePrefix . $RuntimeModel->useTable), + 'conditions' => array( + $model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'), + $RuntimeModel->alias.'.model' => $model->name, + $RuntimeModel->alias.'.locale' => $locale + ) + ); + return $query; + } + + $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']); + $addFields = array(); + if (empty($query['fields'])) { + $addFields = $fields; + } else if (is_array($query['fields'])) { + foreach ($fields as $key => $value) { + $field = (is_numeric($key)) ? $value : $key; + + if (in_array($model->alias.'.*', $query['fields']) || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) { + $addFields[] = $field; + } + } + } + + $this->runtime[$model->alias]['virtualFields'] = $model->virtualFields; + if ($addFields) { + foreach ($addFields as $_f => $field) { + $aliasField = is_numeric($_f) ? $field : $_f; + + foreach (array($aliasField, $model->alias.'.'.$aliasField) as $_field) { + $key = array_search($_field, (array)$query['fields']); + + if ($key !== false) { + unset($query['fields'][$key]); + } + } + + if (is_array($locale)) { + foreach ($locale as $_locale) { + $model->virtualFields['i18n_'.$field.'_'.$_locale] = 'I18n__'.$field.'__'.$_locale.'.content'; + if (!empty($query['fields'])) { + $query['fields'][] = 'i18n_'.$field.'_'.$_locale; + } + $query['joins'][] = array( + 'type' => 'LEFT', + 'alias' => 'I18n__'.$field.'__'.$_locale, + 'table' => $db->fullTableName($tablePrefix . $RuntimeModel->useTable), + 'conditions' => array( + $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"), + 'I18n__'.$field.'__'.$_locale.'.model' => $model->name, + 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $aliasField, + 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale + ) + ); + } + } else { + $model->virtualFields['i18n_'.$field] = 'I18n__'.$field.'.content'; + if (!empty($query['fields'])) { + $query['fields'][] = 'i18n_'.$field; + } + $query['joins'][] = array( + 'type' => 'LEFT', + 'alias' => 'I18n__'.$field, + 'table' => $db->fullTableName($tablePrefix . $RuntimeModel->useTable), + 'conditions' => array( + $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"), + 'I18n__'.$field.'.model' => $model->name, + 'I18n__'.$field.'.'.$RuntimeModel->displayField => $aliasField + ) + ); + + if (is_string($query['conditions'])) { + $query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\''; + } else { + $query['conditions'][$db->name("I18n__{$field}.locale")] = $locale; + } + } + } + } + $this->runtime[$model->alias]['beforeFind'] = $addFields; + return $query; + } + +/** + * afterFind Callback + * + * @param Model $model Model find was run on + * @param array $results Array of model results. + * @param boolean $primary Did the find originate on $model. + * @return array Modified results + */ + public function afterFind($model, $results, $primary) { + $model->virtualFields = $this->runtime[$model->alias]['virtualFields']; + $this->runtime[$model->alias]['virtualFields'] = $this->runtime[$model->alias]['fields'] = array(); + $locale = $this->_getLocale($model); + + if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) { + return $results; + } + $beforeFind = $this->runtime[$model->alias]['beforeFind']; + + foreach ($results as $key => &$row) { + $results[$key][$model->alias]['locale'] = (is_array($locale)) ? current($locale) : $locale; + foreach ($beforeFind as $_f => $field) { + $aliasField = is_numeric($_f) ? $field : $_f; + + if (is_array($locale)) { + foreach ($locale as $_locale) { + if (!isset($row[$model->alias][$aliasField]) && !empty($row[$model->alias]['i18n_'.$field.'_'.$_locale])) { + $row[$model->alias][$aliasField] = $row[$model->alias]['i18n_'.$field.'_'.$_locale]; + $row[$model->alias]['locale'] = $_locale; + } + unset($row[$model->alias]['i18n_'.$field.'_'.$_locale]); + } + + if (!isset($row[$model->alias][$aliasField])) { + $row[$model->alias][$aliasField] = ''; + } + } else { + $value = ''; + if (!empty($row[$model->alias]['i18n_' . $field])) { + $value = $row[$model->alias]['i18n_' . $field]; + } + $row[$model->alias][$aliasField] = $value; + unset($row[$model->alias]['i18n_' . $field]); + } + } + } + return $results; + } + +/** + * beforeValidate Callback + * + * @param Model $model Model invalidFields was called on. + * @return boolean + */ + public function beforeValidate($model) { + $locale = $this->_getLocale($model); + if (empty($locale)) { + return true; + } + $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']); + $tempData = array(); + + foreach ($fields as $key => $value) { + $field = (is_numeric($key)) ? $value : $key; + + if (isset($model->data[$model->alias][$field])) { + $tempData[$field] = $model->data[$model->alias][$field]; + if (is_array($model->data[$model->alias][$field])) { + if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) { + $model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale]; + } else { + $values = array_values($model->data[$model->alias][$field]); + $model->data[$model->alias][$field] = $values[0]; + } + } + } + } + $this->runtime[$model->alias]['beforeSave'] = $tempData; + return true; + } + +/** + * afterSave Callback + * + * @param Model $model Model the callback is called on + * @param boolean $created Whether or not the save created a record. + * @return void + */ + public function afterSave($model, $created) { + if (!isset($this->runtime[$model->alias]['beforeSave'])) { + return true; + } + $locale = $this->_getLocale($model); + $tempData = $this->runtime[$model->alias]['beforeSave']; + unset($this->runtime[$model->alias]['beforeSave']); + $conditions = array('model' => $model->alias, 'foreign_key' => $model->id); + $RuntimeModel = $this->translateModel($model); + + foreach ($tempData as $field => $value) { + unset($conditions['content']); + $conditions['field'] = $field; + if (is_array($value)) { + $conditions['locale'] = array_keys($value); + } else { + $conditions['locale'] = $locale; + if (is_array($locale)) { + $value = array($locale[0] => $value); + } else { + $value = array($locale => $value); + } + } + $translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id'))); + foreach ($value as $_locale => $_value) { + $RuntimeModel->create(); + $conditions['locale'] = $_locale; + $conditions['content'] = $_value; + if (array_key_exists($_locale, $translations)) { + $RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale])))); + } else { + $RuntimeModel->save(array($RuntimeModel->alias => $conditions)); + } + } + } + } + +/** + * afterDelete Callback + * + * @param Model $model Model the callback was run on. + * @return void + */ + public function afterDelete($model) { + $RuntimeModel = $this->translateModel($model); + $conditions = array('model' => $model->alias, 'foreign_key' => $model->id); + $RuntimeModel->deleteAll($conditions); + } + +/** + * Get selected locale for model + * + * @param Model $model Model the locale needs to be set/get on. + * @return mixed string or false + */ + protected function _getLocale($model) { + if (!isset($model->locale) || is_null($model->locale)) { + $I18n = I18n::getInstance(); + $I18n->l10n->get(Configure::read('Config.language')); + $model->locale = $I18n->l10n->locale; + } + + return $model->locale; + } + +/** + * Get instance of model for translations. + * + * If the model has a translateModel property set, this will be used as the class + * name to find/use. If no translateModel property is found 'I18nModel' will be used. + * + * @param Model $model Model to get a translatemodel for. + * @return Model + */ + public function translateModel($model) { + if (!isset($this->runtime[$model->alias]['model'])) { + if (!isset($model->translateModel) || empty($model->translateModel)) { + $className = 'I18nModel'; + } else { + $className = $model->translateModel; + } + + $this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model'); + } + if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) { + $this->runtime[$model->alias]['model']->setSource($model->translateTable); + } elseif (empty($model->translateTable) && empty($model->translateModel)) { + $this->runtime[$model->alias]['model']->setSource('i18n'); + } + return $this->runtime[$model->alias]['model']; + } + +/** + * Bind translation for fields, optionally with hasMany association for + * fake field + * + * @param Model $model instance of model + * @param string|array $fields string with field or array(field1, field2=>AssocName, field3) + * @param boolean $reset + * @return boolean + */ + public function bindTranslation($model, $fields, $reset = true) { + if (is_string($fields)) { + $fields = array($fields); + } + $associations = array(); + $RuntimeModel = $this->translateModel($model); + $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key'); + + foreach ($fields as $key => $value) { + if (is_numeric($key)) { + $field = $value; + $association = null; + } else { + $field = $key; + $association = $value; + } + + if (array_key_exists($field, $this->settings[$model->alias])) { + unset($this->settings[$model->alias][$field]); + } elseif (in_array($field, $this->settings[$model->alias])) { + $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field))); + } + + if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) { + unset($this->runtime[$model->alias]['fields'][$field]); + } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) { + $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field))); + } + + if (is_null($association)) { + if ($reset) { + $this->runtime[$model->alias]['fields'][] = $field; + } else { + $this->settings[$model->alias][] = $field; + } + } else { + if ($reset) { + $this->runtime[$model->alias]['fields'][$field] = $association; + } else { + $this->settings[$model->alias][$field] = $association; + } + + foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) { + if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) { + trigger_error( + __d('cake_dev', 'Association %s is already binded to model %s', $association, $model->alias), + E_USER_ERROR + ); + return false; + } + } + $associations[$association] = array_merge($default, array('conditions' => array( + 'model' => $model->alias, + $RuntimeModel->displayField => $field + ))); + } + } + + if (!empty($associations)) { + $model->bindModel(array('hasMany' => $associations), $reset); + } + return true; + } + +/** + * Unbind translation for fields, optionally unbinds hasMany association for + * fake field + * + * @param Model $model instance of model + * @param mixed $fields string with field, or array(field1, field2=>AssocName, field3), or null for + * unbind all original translations + * @return boolean + */ + public function unbindTranslation($model, $fields = null) { + if (empty($fields) && empty($this->settings[$model->alias])) { + return false; + } + if (empty($fields)) { + return $this->unbindTranslation($model, $this->settings[$model->alias]); + } + + if (is_string($fields)) { + $fields = array($fields); + } + $RuntimeModel = $this->translateModel($model); + $associations = array(); + + foreach ($fields as $key => $value) { + if (is_numeric($key)) { + $field = $value; + $association = null; + } else { + $field = $key; + $association = $value; + } + + if (array_key_exists($field, $this->settings[$model->alias])) { + unset($this->settings[$model->alias][$field]); + } elseif (in_array($field, $this->settings[$model->alias])) { + $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field))); + } + + if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) { + unset($this->runtime[$model->alias]['fields'][$field]); + } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) { + $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field))); + } + + if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) { + $associations[] = $association; + } + } + + if (!empty($associations)) { + $model->unbindModel(array('hasMany' => $associations), false); + } + return true; + } +} + +/** + * @package Cake.Model.Behavior + */ +class I18nModel extends AppModel { + +/** + * Model name + * + * @var string + */ + public $name = 'I18nModel'; + +/** + * Table name + * + * @var string + */ + public $useTable = 'i18n'; + +/** + * Display field + * + * @var string + */ + public $displayField = 'field'; + +} diff --git a/app/Cake/Model/Behavior/TreeBehavior.php b/app/Cake/Model/Behavior/TreeBehavior.php new file mode 100644 index 00000000..6e087545 --- /dev/null +++ b/app/Cake/Model/Behavior/TreeBehavior.php @@ -0,0 +1,964 @@ + 'parent_id', 'left' => 'lft', 'right' => 'rght', + 'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1 + ); + +/** + * Initiate Tree behavior + * + * @param Model $Model instance of model + * @param array $config array of configuration settings. + * @return void + */ + public function setup($Model, $config = array()) { + if (!is_array($config)) { + $config = array('type' => $config); + } + $settings = array_merge($this->_defaults, $config); + + if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) { + $data = $Model->getAssociated($settings['scope']); + $parent = $Model->{$settings['scope']}; + $settings['scope'] = $Model->alias . '.' . $data['foreignKey'] . ' = ' . $parent->alias . '.' . $parent->primaryKey; + $settings['recursive'] = 0; + } + $this->settings[$Model->alias] = $settings; + } + +/** + * After save method. Called after all saves + * + * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the + * parameters to be saved. + * + * @param Model $Model Model instance. + * @param boolean $created indicates whether the node just saved was created or updated + * @return boolean true on success, false on failure + */ + public function afterSave($Model, $created) { + extract($this->settings[$Model->alias]); + if ($created) { + if ((isset($Model->data[$Model->alias][$parent])) && $Model->data[$Model->alias][$parent]) { + return $this->_setParent($Model, $Model->data[$Model->alias][$parent], $created); + } + } elseif ($__parentChange) { + $this->settings[$Model->alias]['__parentChange'] = false; + return $this->_setParent($Model, $Model->data[$Model->alias][$parent]); + } + } + +/** + * Before delete method. Called before all deletes + * + * Will delete the current node and all children using the deleteAll method and sync the table + * + * @param Model $Model Model instance + * @param boolean $cascade + * @return boolean true to continue, false to abort the delete + */ + public function beforeDelete($Model, $cascade = true) { + extract($this->settings[$Model->alias]); + list($name, $data) = array($Model->alias, $Model->read()); + $data = $data[$name]; + + if (!$data[$right] || !$data[$left]) { + return true; + } + $diff = $data[$right] - $data[$left] + 1; + + if ($diff > 2) { + if (is_string($scope)) { + $scope = array($scope); + } + $scope[]["{$Model->alias}.{$left} BETWEEN ? AND ?"] = array($data[$left] + 1, $data[$right] - 1); + $Model->deleteAll($scope); + } + $this->_sync($Model, $diff, '-', '> ' . $data[$right]); + return true; + } + +/** + * Before save method. Called before all saves + * + * Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the + * parameters to be saved. For newly created nodes with NO parent the left and right field values are set directly by + * this method bypassing the setParent logic. + * + * @since 1.2 + * @param Model $Model Model instance + * @return boolean true to continue, false to abort the save + */ + public function beforeSave($Model) { + extract($this->settings[$Model->alias]); + + $this->_addToWhitelist($Model, array($left, $right)); + if (!$Model->id) { + if (array_key_exists($parent, $Model->data[$Model->alias]) && $Model->data[$Model->alias][$parent]) { + $parentNode = $Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]), + 'fields' => array($Model->primaryKey, $right), 'recursive' => $recursive + )); + if (!$parentNode) { + return false; + } + list($parentNode) = array_values($parentNode); + $Model->data[$Model->alias][$left] = 0; //$parentNode[$right]; + $Model->data[$Model->alias][$right] = 0; //$parentNode[$right] + 1; + } else { + $edge = $this->_getMax($Model, $scope, $right, $recursive); + $Model->data[$Model->alias][$left] = $edge + 1; + $Model->data[$Model->alias][$right] = $edge + 2; + } + } elseif (array_key_exists($parent, $Model->data[$Model->alias])) { + if ($Model->data[$Model->alias][$parent] != $Model->field($parent)) { + $this->settings[$Model->alias]['__parentChange'] = true; + } + if (!$Model->data[$Model->alias][$parent]) { + $Model->data[$Model->alias][$parent] = null; + $this->_addToWhitelist($Model, $parent); + } else { + $values = $Model->find('first', array( + 'conditions' => array($scope,$Model->escapeField() => $Model->id), + 'fields' => array($Model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive) + ); + + if ($values === false) { + return false; + } + list($node) = array_values($values); + + $parentNode = $Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]), + 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive + )); + if (!$parentNode) { + return false; + } + list($parentNode) = array_values($parentNode); + + if (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) { + return false; + } elseif ($node[$Model->primaryKey] == $parentNode[$Model->primaryKey]) { + return false; + } + } + } + return true; + } + +/** + * Get the number of child nodes + * + * If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field) + * If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted. + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to read or false to read all top level nodes + * @param boolean $direct whether to count direct, or all, children + * @return integer number of child nodes + * @link http://book.cakephp.org/view/1347/Counting-children + */ + public function childCount($Model, $id = null, $direct = false) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + if ($id === null && $Model->id) { + $id = $Model->id; + } elseif (!$id) { + $id = null; + } + extract($this->settings[$Model->alias]); + + if ($direct) { + return $Model->find('count', array('conditions' => array($scope, $Model->escapeField($parent) => $id))); + } + + if ($id === null) { + return $Model->find('count', array('conditions' => $scope)); + } elseif ($Model->id === $id && isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) { + $data = $Model->data[$Model->alias]; + } else { + $data = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'recursive' => $recursive)); + if (!$data) { + return 0; + } + $data = $data[$Model->alias]; + } + return ($data[$right] - $data[$left] - 1) / 2; + } + +/** + * Get the child nodes of the current model + * + * If the direct parameter is set to true, only the direct children are returned (based upon the parent_id field) + * If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted. + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to read + * @param boolean $direct whether to return only the direct, or all, children + * @param mixed $fields Either a single string of a field name, or an array of field names + * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order + * @param integer $limit SQL LIMIT clause, for calculating items per page. + * @param integer $page Page number, for accessing paged data + * @param integer $recursive The number of levels deep to fetch associated records + * @return array Array of child nodes + * @link http://book.cakephp.org/view/1346/Children + */ + public function children($Model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + $overrideRecursive = $recursive; + + if ($id === null && $Model->id) { + $id = $Model->id; + } elseif (!$id) { + $id = null; + } + $name = $Model->alias; + extract($this->settings[$Model->alias]); + + if (!is_null($overrideRecursive)) { + $recursive = $overrideRecursive; + } + if (!$order) { + $order = $Model->alias . '.' . $left . ' asc'; + } + if ($direct) { + $conditions = array($scope, $Model->escapeField($parent) => $id); + return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } + + if (!$id) { + $conditions = $scope; + } else { + $result = array_values((array)$Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $id), + 'fields' => array($left, $right), + 'recursive' => $recursive + ))); + + if (empty($result) || !isset($result[0])) { + return array(); + } + $conditions = array($scope, + $Model->escapeField($right) . ' <' => $result[0][$right], + $Model->escapeField($left) . ' >' => $result[0][$left] + ); + } + return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } + +/** + * A convenience method for returning a hierarchical array used for HTML select boxes + * + * @param Model $Model Model instance + * @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...) + * @param string $keyPath A string path to the key, i.e. "{n}.Post.id" + * @param string $valuePath A string path to the value, i.e. "{n}.Post.title" + * @param string $spacer The character or characters which will be repeated + * @param integer $recursive The number of levels deep to fetch associated records + * @return array An associative array of records, where the id is the key, and the display field is the value + * @link http://book.cakephp.org/view/1348/generatetreelist + */ + public function generateTreeList($Model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null) { + $overrideRecursive = $recursive; + extract($this->settings[$Model->alias]); + if (!is_null($overrideRecursive)) { + $recursive = $overrideRecursive; + } + + if ($keyPath == null && $valuePath == null && $Model->hasField($Model->displayField)) { + $fields = array($Model->primaryKey, $Model->displayField, $left, $right); + } else { + $fields = null; + } + + if ($keyPath == null) { + $keyPath = '{n}.' . $Model->alias . '.' . $Model->primaryKey; + } + + if ($valuePath == null) { + $valuePath = array('{0}{1}', '{n}.tree_prefix', '{n}.' . $Model->alias . '.' . $Model->displayField); + + } elseif (is_string($valuePath)) { + $valuePath = array('{0}{1}', '{n}.tree_prefix', $valuePath); + + } else { + $valuePath[0] = '{' . (count($valuePath) - 1) . '}' . $valuePath[0]; + $valuePath[] = '{n}.tree_prefix'; + } + $order = $Model->alias . '.' . $left . ' asc'; + $results = $Model->find('all', compact('conditions', 'fields', 'order', 'recursive')); + $stack = array(); + + foreach ($results as $i => $result) { + while ($stack && ($stack[count($stack) - 1] < $result[$Model->alias][$right])) { + array_pop($stack); + } + $results[$i]['tree_prefix'] = str_repeat($spacer,count($stack)); + $stack[] = $result[$Model->alias][$right]; + } + if (empty($results)) { + return array(); + } + return Set::combine($results, $keyPath, $valuePath); + } + +/** + * Get the parent node + * + * reads the parent id and returns this node + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to read + * @param string|array $fields + * @param integer $recursive The number of levels deep to fetch associated records + * @return array|boolean Array of data for the parent node + * @link http://book.cakephp.org/view/1349/getparentnode + */ + public function getParentNode($Model, $id = null, $fields = null, $recursive = null) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + $overrideRecursive = $recursive; + if (empty ($id)) { + $id = $Model->id; + } + extract($this->settings[$Model->alias]); + if (!is_null($overrideRecursive)) { + $recursive = $overrideRecursive; + } + $parentId = $Model->find('first', array('conditions' => array($Model->primaryKey => $id), 'fields' => array($parent), 'recursive' => -1)); + + if ($parentId) { + $parentId = $parentId[$Model->alias][$parent]; + $parent = $Model->find('first', array('conditions' => array($Model->escapeField() => $parentId), 'fields' => $fields, 'recursive' => $recursive)); + + return $parent; + } + return false; + } + +/** + * Get the path to the given node + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to read + * @param mixed $fields Either a single string of a field name, or an array of field names + * @param integer $recursive The number of levels deep to fetch associated records + * @return array Array of nodes from top most parent to current node + * @link http://book.cakephp.org/view/1350/getpath + */ + public function getPath($Model, $id = null, $fields = null, $recursive = null) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + $overrideRecursive = $recursive; + if (empty ($id)) { + $id = $Model->id; + } + extract($this->settings[$Model->alias]); + if (!is_null($overrideRecursive)) { + $recursive = $overrideRecursive; + } + $result = $Model->find('first', array('conditions' => array($Model->escapeField() => $id), 'fields' => array($left, $right), 'recursive' => $recursive)); + if ($result) { + $result = array_values($result); + } else { + return null; + } + $item = $result[0]; + $results = $Model->find('all', array( + 'conditions' => array($scope, $Model->escapeField($left) . ' <=' => $item[$left], $Model->escapeField($right) . ' >=' => $item[$right]), + 'fields' => $fields, 'order' => array($Model->escapeField($left) => 'asc'), 'recursive' => $recursive + )); + return $results; + } + +/** + * Reorder the node without changing the parent. + * + * If the node is the last child, or is a top level node with no subsequent node this method will return false + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to move + * @param integer|boolean $number how many places to move the node or true to move to last position + * @return boolean true on success, false on failure + * @link http://book.cakephp.org/view/1352/moveDown + */ + public function moveDown($Model, $id = null, $number = 1) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + if (!$number) { + return false; + } + if (empty ($id)) { + $id = $Model->id; + } + extract($this->settings[$Model->alias]); + list($node) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $id), + 'fields' => array($Model->primaryKey, $left, $right, $parent), 'recursive' => $recursive + ))); + if ($node[$parent]) { + list($parentNode) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $node[$parent]), + 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive + ))); + if (($node[$right] + 1) == $parentNode[$right]) { + return false; + } + } + $nextNode = $Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField($left) => ($node[$right] + 1)), + 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive) + ); + if ($nextNode) { + list($nextNode) = array_values($nextNode); + } else { + return false; + } + $edge = $this->_getMax($Model, $scope, $right, $recursive); + $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]); + $this->_sync($Model, $nextNode[$left] - $node[$left], '-', 'BETWEEN ' . $nextNode[$left] . ' AND ' . $nextNode[$right]); + $this->_sync($Model, $edge - $node[$left] - ($nextNode[$right] - $nextNode[$left]), '-', '> ' . $edge); + + if (is_int($number)) { + $number--; + } + if ($number) { + $this->moveDown($Model, $id, $number); + } + return true; + } + +/** + * Reorder the node without changing the parent. + * + * If the node is the first child, or is a top level node with no previous node this method will return false + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to move + * @param integer|boolean $number how many places to move the node, or true to move to first position + * @return boolean true on success, false on failure + * @link http://book.cakephp.org/view/1353/moveUp + */ + public function moveUp($Model, $id = null, $number = 1) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + if (!$number) { + return false; + } + if (empty ($id)) { + $id = $Model->id; + } + extract($this->settings[$Model->alias]); + list($node) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $id), + 'fields' => array($Model->primaryKey, $left, $right, $parent ), 'recursive' => $recursive + ))); + if ($node[$parent]) { + list($parentNode) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $node[$parent]), + 'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive + ))); + if (($node[$left] - 1) == $parentNode[$left]) { + return false; + } + } + $previousNode = $Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField($right) => ($node[$left] - 1)), + 'fields' => array($Model->primaryKey, $left, $right), + 'recursive' => $recursive + )); + + if ($previousNode) { + list($previousNode) = array_values($previousNode); + } else { + return false; + } + $edge = $this->_getMax($Model, $scope, $right, $recursive); + $this->_sync($Model, $edge - $previousNode[$left] +1, '+', 'BETWEEN ' . $previousNode[$left] . ' AND ' . $previousNode[$right]); + $this->_sync($Model, $node[$left] - $previousNode[$left], '-', 'BETWEEN ' .$node[$left] . ' AND ' . $node[$right]); + $this->_sync($Model, $edge - $previousNode[$left] - ($node[$right] - $node[$left]), '-', '> ' . $edge); + if (is_int($number)) { + $number--; + } + if ($number) { + $this->moveUp($Model, $id, $number); + } + return true; + } + +/** + * Recover a corrupted tree + * + * The mode parameter is used to specify the source of info that is valid/correct. The opposite source of data + * will be populated based upon that source of info. E.g. if the MPTT fields are corrupt or empty, with the $mode + * 'parent' the values of the parent_id field will be used to populate the left and right fields. The missingParentAction + * parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present. + * + * @todo Could be written to be faster, *maybe*. Ideally using a subquery and putting all the logic burden on the DB. + * @param Model $Model Model instance + * @param string $mode parent or tree + * @param mixed $missingParentAction 'return' to do nothing and return, 'delete' to + * delete, or the id of the parent to set as the parent_id + * @return boolean true on success, false on failure + * @link http://book.cakephp.org/view/1628/Recover + */ + public function recover($Model, $mode = 'parent', $missingParentAction = null) { + if (is_array($mode)) { + extract (array_merge(array('mode' => 'parent'), $mode)); + } + extract($this->settings[$Model->alias]); + $Model->recursive = $recursive; + if ($mode == 'parent') { + $Model->bindModel(array('belongsTo' => array('VerifyParent' => array( + 'className' => $Model->alias, + 'foreignKey' => $parent, + 'fields' => array($Model->primaryKey, $left, $right, $parent), + )))); + $missingParents = $Model->find('list', array( + 'recursive' => 0, + 'conditions' => array($scope, array( + 'NOT' => array($Model->escapeField($parent) => null), $Model->VerifyParent->escapeField() => null + )) + )); + $Model->unbindModel(array('belongsTo' => array('VerifyParent'))); + if ($missingParents) { + if ($missingParentAction == 'return') { + foreach ($missingParents as $id => $display) { + $this->errors[] = 'cannot find the parent for ' . $Model->alias . ' with id ' . $id . '(' . $display . ')'; + + } + return false; + } elseif ($missingParentAction == 'delete') { + $Model->deleteAll(array($Model->primaryKey => array_flip($missingParents))); + } else { + $Model->updateAll(array($parent => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents))); + } + } + $count = 1; + foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey), 'order' => $left)) as $array) { + $Model->id = $array[$Model->alias][$Model->primaryKey]; + $lft = $count++; + $rght = $count++; + $Model->save(array($left => $lft, $right => $rght), array('callbacks' => false)); + } + foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) { + $Model->create(); + $Model->id = $array[$Model->alias][$Model->primaryKey]; + $this->_setParent($Model, $array[$Model->alias][$parent]); + } + } else { + $db = ConnectionManager::getDataSource($Model->useDbConfig); + foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) { + $path = $this->getPath($Model, $array[$Model->alias][$Model->primaryKey]); + if ($path == null || count($path) < 2) { + $parentId = null; + } else { + $parentId = $path[count($path) - 2][$Model->alias][$Model->primaryKey]; + } + $Model->updateAll(array($parent => $db->value($parentId, $parent)), array($Model->escapeField() => $array[$Model->alias][$Model->primaryKey])); + } + } + return true; + } + +/** + * Reorder method. + * + * Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters. + * This method does not change the parent of any node. + * + * Requires a valid tree, by default it verifies the tree before beginning. + * + * Options: + * + * - 'id' id of record to use as top node for reordering + * - 'field' Which field to use in reordeing defaults to displayField + * - 'order' Direction to order either DESC or ASC (defaults to ASC) + * - 'verify' Whether or not to verify the tree before reorder. defaults to true. + * + * @param Model $Model Model instance + * @param array $options array of options to use in reordering. + * @return boolean true on success, false on failure + * @link http://book.cakephp.org/view/1355/reorder + * @link http://book.cakephp.org/view/1629/Reorder + */ + public function reorder($Model, $options = array()) { + $options = array_merge(array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true), $options); + extract($options); + if ($verify && !$this->verify($Model)) { + return false; + } + $verify = false; + extract($this->settings[$Model->alias]); + $fields = array($Model->primaryKey, $field, $left, $right); + $sort = $field . ' ' . $order; + $nodes = $this->children($Model, $id, true, $fields, $sort, null, null, $recursive); + + $cacheQueries = $Model->cacheQueries; + $Model->cacheQueries = false; + if ($nodes) { + foreach ($nodes as $node) { + $id = $node[$Model->alias][$Model->primaryKey]; + $this->moveDown($Model, $id, true); + if ($node[$Model->alias][$left] != $node[$Model->alias][$right] - 1) { + $this->reorder($Model, compact('id', 'field', 'order', 'verify')); + } + } + } + $Model->cacheQueries = $cacheQueries; + return true; + } + +/** + * Remove the current node from the tree, and reparent all children up one level. + * + * If the parameter delete is false, the node will become a new top level node. Otherwise the node will be deleted + * after the children are reparented. + * + * @param Model $Model Model instance + * @param mixed $id The ID of the record to remove + * @param boolean $delete whether to delete the node after reparenting children (if any) + * @return boolean true on success, false on failure + * @link http://book.cakephp.org/view/1354/removeFromTree + */ + public function removeFromTree($Model, $id = null, $delete = false) { + if (is_array($id)) { + extract (array_merge(array('id' => null), $id)); + } + extract($this->settings[$Model->alias]); + + list($node) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $id), + 'fields' => array($Model->primaryKey, $left, $right, $parent), + 'recursive' => $recursive + ))); + + if ($node[$right] == $node[$left] + 1) { + if ($delete) { + return $Model->delete($id); + } else { + $Model->id = $id; + return $Model->saveField($parent, null); + } + } elseif ($node[$parent]) { + list($parentNode) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $node[$parent]), + 'fields' => array($Model->primaryKey, $left, $right), + 'recursive' => $recursive + ))); + } else { + $parentNode[$right] = $node[$right] + 1; + } + + $db = ConnectionManager::getDataSource($Model->useDbConfig); + $Model->updateAll( + array($parent => $db->value($node[$parent], $parent)), + array($Model->escapeField($parent) => $node[$Model->primaryKey]) + ); + $this->_sync($Model, 1, '-', 'BETWEEN ' . ($node[$left] + 1) . ' AND ' . ($node[$right] - 1)); + $this->_sync($Model, 2, '-', '> ' . ($node[$right])); + $Model->id = $id; + + if ($delete) { + $Model->updateAll( + array( + $Model->escapeField($left) => 0, + $Model->escapeField($right) => 0, + $Model->escapeField($parent) => null + ), + array($Model->escapeField() => $id) + ); + return $Model->delete($id); + } else { + $edge = $this->_getMax($Model, $scope, $right, $recursive); + if ($node[$right] == $edge) { + $edge = $edge - 2; + } + $Model->id = $id; + return $Model->save( + array($left => $edge + 1, $right => $edge + 2, $parent => null), + array('callbacks' => false) + ); + } + } + +/** + * Check if the current tree is valid. + * + * Returns true if the tree is valid otherwise an array of (type, incorrect left/right index, message) + * + * @param Model $Model Model instance + * @return mixed true if the tree is valid or empty, otherwise an array of (error type [index, node], + * [incorrect left/right index,node id], message) + * @link http://book.cakephp.org/view/1630/Verify + */ + public function verify($Model) { + extract($this->settings[$Model->alias]); + if (!$Model->find('count', array('conditions' => $scope))) { + return true; + } + $min = $this->_getMin($Model, $scope, $left, $recursive); + $edge = $this->_getMax($Model, $scope, $right, $recursive); + $errors = array(); + + for ($i = $min; $i <= $edge; $i++) { + $count = $Model->find('count', array('conditions' => array( + $scope, 'OR' => array($Model->escapeField($left) => $i, $Model->escapeField($right) => $i) + ))); + if ($count != 1) { + if ($count == 0) { + $errors[] = array('index', $i, 'missing'); + } else { + $errors[] = array('index', $i, 'duplicate'); + } + } + } + $node = $Model->find('first', array('conditions' => array($scope, $Model->escapeField($right) . '< ' . $Model->escapeField($left)), 'recursive' => 0)); + if ($node) { + $errors[] = array('node', $node[$Model->alias][$Model->primaryKey], 'left greater than right.'); + } + + $Model->bindModel(array('belongsTo' => array('VerifyParent' => array( + 'className' => $Model->alias, + 'foreignKey' => $parent, + 'fields' => array($Model->primaryKey, $left, $right, $parent) + )))); + + foreach ($Model->find('all', array('conditions' => $scope, 'recursive' => 0)) as $instance) { + if (is_null($instance[$Model->alias][$left]) || is_null($instance[$Model->alias][$right])) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], + 'has invalid left or right values'); + } elseif ($instance[$Model->alias][$left] == $instance[$Model->alias][$right]) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], + 'left and right values identical'); + } elseif ($instance[$Model->alias][$parent]) { + if (!$instance['VerifyParent'][$Model->primaryKey]) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], + 'The parent node ' . $instance[$Model->alias][$parent] . ' doesn\'t exist'); + } elseif ($instance[$Model->alias][$left] < $instance['VerifyParent'][$left]) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], + 'left less than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').'); + } elseif ($instance[$Model->alias][$right] > $instance['VerifyParent'][$right]) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], + 'right greater than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').'); + } + } elseif ($Model->find('count', array('conditions' => array($scope, $Model->escapeField($left) . ' <' => $instance[$Model->alias][$left], $Model->escapeField($right) . ' >' => $instance[$Model->alias][$right]), 'recursive' => 0))) { + $errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], 'The parent field is blank, but has a parent'); + } + } + if ($errors) { + return $errors; + } + return true; + } + +/** + * Sets the parent of the given node + * + * The force parameter is used to override the "don't change the parent to the current parent" logic in the event + * of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this + * method could be private, since calling save with parent_id set also calls setParent + * + * @param Model $Model Model instance + * @param mixed $parentId + * @param boolean $created + * @return boolean true on success, false on failure + */ + protected function _setParent($Model, $parentId = null, $created = false) { + extract($this->settings[$Model->alias]); + list($node) = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $Model->id), + 'fields' => array($Model->primaryKey, $parent, $left, $right), + 'recursive' => $recursive + ))); + $edge = $this->_getMax($Model, $scope, $right, $recursive, $created); + + if (empty ($parentId)) { + $this->_sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created); + $this->_sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created); + } else { + $values = $Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $parentId), + 'fields' => array($Model->primaryKey, $left, $right), + 'recursive' => $recursive + )); + + if ($values === false) { + return false; + } + $parentNode = array_values($values); + + if (empty($parentNode) || empty($parentNode[0])) { + return false; + } + $parentNode = $parentNode[0]; + + if (($Model->id == $parentId)) { + return false; + + } elseif (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) { + return false; + } + if (empty ($node[$left]) && empty ($node[$right])) { + $this->_sync($Model, 2, '+', '>= ' . $parentNode[$right], $created); + $result = $Model->save( + array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId), + array('validate' => false, 'callbacks' => false) + ); + $Model->data = $result; + } else { + $this->_sync($Model, $edge - $node[$left] +1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created); + $diff = $node[$right] - $node[$left] + 1; + + if ($node[$left] > $parentNode[$left]) { + if ($node[$right] < $parentNode[$right]) { + $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created); + $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created); + } else { + $this->_sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created); + $this->_sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created); + } + } else { + $this->_sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created); + $this->_sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created); + } + } + } + return true; + } + +/** + * get the maximum index value in the table. + * + * @param Model $Model + * @param string $scope + * @param string $right + * @param integer $recursive + * @param boolean $created + * @return integer + */ + protected function _getMax($Model, $scope, $right, $recursive = -1, $created = false) { + $db = ConnectionManager::getDataSource($Model->useDbConfig); + if ($created) { + if (is_string($scope)) { + $scope .= " AND {$Model->alias}.{$Model->primaryKey} <> "; + $scope .= $db->value($Model->id, $Model->getColumnType($Model->primaryKey)); + } else { + $scope['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id; + } + } + $name = $Model->alias . '.' . $right; + list($edge) = array_values($Model->find('first', array( + 'conditions' => $scope, + 'fields' => $db->calculate($Model, 'max', array($name, $right)), + 'recursive' => $recursive + ))); + return (empty($edge[$right])) ? 0 : $edge[$right]; + } + +/** + * get the minimum index value in the table. + * + * @param Model $Model + * @param string $scope + * @param string $left + * @param integer $recursive + * @return integer + */ + protected function _getMin($Model, $scope, $left, $recursive = -1) { + $db = ConnectionManager::getDataSource($Model->useDbConfig); + $name = $Model->alias . '.' . $left; + list($edge) = array_values($Model->find('first', array( + 'conditions' => $scope, + 'fields' => $db->calculate($Model, 'min', array($name, $left)), + 'recursive' => $recursive + ))); + return (empty($edge[$left])) ? 0 : $edge[$left]; + } + +/** + * Table sync method. + * + * Handles table sync operations, Taking account of the behavior scope. + * + * @param Model $Model + * @param integer $shift + * @param string $dir + * @param array $conditions + * @param boolean $created + * @param string $field + * @return void + */ + protected function _sync($Model, $shift, $dir = '+', $conditions = array(), $created = false, $field = 'both') { + $ModelRecursive = $Model->recursive; + extract($this->settings[$Model->alias]); + $Model->recursive = $recursive; + + if ($field == 'both') { + $this->_sync($Model, $shift, $dir, $conditions, $created, $left); + $field = $right; + } + if (is_string($conditions)) { + $conditions = array("{$Model->alias}.{$field} {$conditions}"); + } + if (($scope != '1 = 1' && $scope !== true) && $scope) { + $conditions[] = $scope; + } + if ($created) { + $conditions['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id; + } + $Model->updateAll(array($Model->alias . '.' . $field => $Model->escapeField($field) . ' ' . $dir . ' ' . $shift), $conditions); + $Model->recursive = $ModelRecursive; + } +} diff --git a/app/Cake/Model/BehaviorCollection.php b/app/Cake/Model/BehaviorCollection.php new file mode 100644 index 00000000..086fba3d --- /dev/null +++ b/app/Cake/Model/BehaviorCollection.php @@ -0,0 +1,275 @@ +modelName = $modelName; + + if (!empty($behaviors)) { + foreach (BehaviorCollection::normalizeObjectArray($behaviors) as $behavior => $config) { + $this->load($config['class'], $config['settings']); + } + } + } + +/** + * Backwards compatible alias for load() + * + * @param string $behavior + * @param array $config + * @return void + * @deprecated Replaced with load() + */ + public function attach($behavior, $config = array()) { + return $this->load($behavior, $config); + } + +/** + * Loads a behavior into the collection. You can use use `$config['enabled'] = false` + * to load a behavior with callbacks disabled. By default callbacks are enabled. Disable behaviors + * can still be used as normal. + * + * You can alias your behavior as an existing behavior by setting the 'className' key, i.e., + * {{{ + * public $actsAs = array( + * 'Tree' => array( + * 'className' => 'AliasedTree' + * ); + * ); + * }}} + * All calls to the `Tree` behavior would use `AliasedTree` instead. + * + * @param string $behavior CamelCased name of the behavior to load + * @param array $config Behavior configuration parameters + * @return boolean True on success, false on failure + * @throws MissingBehaviorClassException when a behavior could not be found. + */ + public function load($behavior, $config = array()) { + if (is_array($config) && isset($config['className'])) { + $alias = $behavior; + $behavior = $config['className']; + } + list($plugin, $name) = pluginSplit($behavior, true); + if (!isset($alias)) { + $alias = $name; + } + + $class = $name . 'Behavior'; + + App::uses($class, $plugin . 'Model/Behavior'); + if (!class_exists($class)) { + throw new MissingBehaviorClassException(array( + 'file' => Inflector::underscore($behavior) . '.php', + 'class' => $class + )); + } + + if (!isset($this->{$alias})) { + if (ClassRegistry::isKeySet($class)) { + $this->_loaded[$alias] = ClassRegistry::getObject($class); + } else { + $this->_loaded[$alias] = new $class(); + ClassRegistry::addObject($class, $this->_loaded[$alias]); + if (!empty($plugin)) { + ClassRegistry::addObject($plugin . '.' . $class, $this->_loaded[$alias]); + } + } + } elseif (isset($this->_loaded[$alias]->settings) && isset($this->_loaded[$alias]->settings[$this->modelName])) { + if ($config !== null && $config !== false) { + $config = array_merge($this->_loaded[$alias]->settings[$this->modelName], $config); + } else { + $config = array(); + } + } + if (empty($config)) { + $config = array(); + } + $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $config); + + foreach ($this->_loaded[$alias]->mapMethods as $method => $methodAlias) { + $this->_mappedMethods[$method] = array($alias, $methodAlias); + } + $methods = get_class_methods($this->_loaded[$alias]); + $parentMethods = array_flip(get_class_methods('ModelBehavior')); + $callbacks = array( + 'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave', + 'beforeDelete', 'afterDelete', 'onError' + ); + + foreach ($methods as $m) { + if (!isset($parentMethods[$m])) { + $methodAllowed = ( + $m[0] != '_' && !array_key_exists($m, $this->_methods) && + !in_array($m, $callbacks) + ); + if ($methodAllowed) { + $this->_methods[$m] = array($alias, $m); + } + } + } + + $configDisabled = isset($config['enabled']) && $config['enabled'] === false; + if (!in_array($alias, $this->_enabled) && !$configDisabled) { + $this->enable($alias); + } elseif ($configDisabled) { + $this->disable($alias); + } + return true; + } + +/** + * Detaches a behavior from a model + * + * @param string $name CamelCased name of the behavior to unload + * @return void + */ + public function unload($name) { + list($plugin, $name) = pluginSplit($name); + if (isset($this->_loaded[$name])) { + $this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName)); + unset($this->_loaded[$name]); + } + foreach ($this->_methods as $m => $callback) { + if (is_array($callback) && $callback[0] == $name) { + unset($this->_methods[$m]); + } + } + $this->_enabled = array_values(array_diff($this->_enabled, (array)$name)); + } + +/** + * Backwards compatible alias for unload() + * + * @param string $name Name of behavior + * @return void + * @deprecated Use unload instead. + */ + public function detach($name) { + return $this->unload($name); + } + +/** + * Dispatches a behavior method. Will call either normal methods or mapped methods. + * + * If a method is not handeled by the BehaviorCollection, and $strict is false, a + * special return of `array('unhandled')` will be returned to signal the method was not found. + * + * @param Model $model The model the method was originally called on. + * @param string $method The method called. + * @param array $params Parameters for the called method. + * @param boolean $strict If methods are not found, trigger an error. + * @return array All methods for all behaviors attached to this object + */ + public function dispatchMethod($model, $method, $params = array(), $strict = false) { + $method = $this->hasMethod($method, true); + + if ($strict && empty($method)) { + trigger_error(__d('cake_dev', "BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", $method), E_USER_WARNING); + return null; + } + if (empty($method)) { + return array('unhandled'); + } + if (count($method) === 3) { + array_unshift($params, $method[2]); + unset($method[2]); + } + return call_user_func_array( + array($this->_loaded[$method[0]], $method[1]), + array_merge(array(&$model), $params) + ); + } + +/** + * Gets the method list for attached behaviors, i.e. all public, non-callback methods. + * This does not include mappedMethods. + * + * @return array All public methods for all behaviors attached to this collection + */ + public function methods() { + return $this->_methods; + } + +/** + * Check to see if a behavior in this collection implements the provided method. Will + * also check mappedMethods. + * + * @param string $method The method to find. + * @param boolean $callback Return the callback for the method. + * @return mixed If $callback is false, a boolean will be returnned, if its true, an array + * containing callback information will be returnned. For mapped methods the array will have 3 elements. + */ + public function hasMethod($method, $callback = false) { + if (isset($this->_methods[$method])) { + return $callback ? $this->_methods[$method] : true; + } + foreach ($this->_mappedMethods as $pattern => $target) { + if (preg_match($pattern . 'i', $method)) { + if ($callback) { + $target[] = $method; + return $target; + } + return true; + } + } + return false; + } + +} diff --git a/app/Cake/Model/CakeSchema.php b/app/Cake/Model/CakeSchema.php new file mode 100644 index 00000000..4ede1468 --- /dev/null +++ b/app/Cake/Model/CakeSchema.php @@ -0,0 +1,697 @@ +name = preg_replace('/schema$/i', '', get_class($this)); + } + if (!empty($options['plugin'])) { + $this->plugin = $options['plugin']; + } + + if (strtolower($this->name) === 'cake') { + $this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir'))); + } + + if (empty($options['path'])) { + $this->path = APP . 'Config' . DS . 'Schema'; + } + + $options = array_merge(get_object_vars($this), $options); + $this->build($options); + } + +/** + * Builds schema object properties + * + * @param array $data loaded object properties + * @return void + */ + public function build($data) { + $file = null; + foreach ($data as $key => $val) { + if (!empty($val)) { + if (!in_array($key, array('plugin', 'name', 'path', 'file', 'connection', 'tables', '_log'))) { + if ($key[0] === '_') { + continue; + } + $this->tables[$key] = $val; + unset($this->{$key}); + } elseif ($key !== 'tables') { + if ($key === 'name' && $val !== $this->name && !isset($data['file'])) { + $file = Inflector::underscore($val) . '.php'; + } + $this->{$key} = $val; + } + } + } + if (file_exists($this->path . DS . $file) && is_file($this->path . DS . $file)) { + $this->file = $file; + } elseif (!empty($this->plugin)) { + $this->path = CakePlugin::path($this->plugin) . 'Config' . DS . 'Schema'; + } + } + +/** + * Before callback to be implemented in subclasses + * + * @param array $event schema object properties + * @return boolean Should process continue + */ + public function before($event = array()) { + return true; + } + +/** + * After callback to be implemented in subclasses + * + * @param array $event schema object properties + * @return void + */ + public function after($event = array()) { + } + +/** + * Reads database and creates schema tables + * + * @param array $options schema object properties + * @return array Set of name and tables + */ + public function load($options = array()) { + if (is_string($options)) { + $options = array('path' => $options); + } + + $this->build($options); + extract(get_object_vars($this)); + + $class = $name .'Schema'; + + if (!class_exists($class)) { + if (file_exists($path . DS . $file) && is_file($path . DS . $file)) { + require_once($path . DS . $file); + } elseif (file_exists($path . DS . 'schema.php') && is_file($path . DS . 'schema.php')) { + require_once($path . DS . 'schema.php'); + } + } + + if (class_exists($class)) { + $Schema = new $class($options); + return $Schema; + } + + return false; + } + +/** + * Reads database and creates schema tables + * + * Options + * + * - 'connection' - the db connection to use + * - 'name' - name of the schema + * - 'models' - a list of models to use, or false to ignore models + * + * @param array $options schema object properties + * @return array Array indexed by name and tables + */ + public function read($options = array()) { + extract(array_merge( + array( + 'connection' => $this->connection, + 'name' => $this->name, + 'models' => true, + ), + $options + )); + $db = ConnectionManager::getDataSource($connection); + + if (isset($this->plugin)) { + App::uses($this->plugin . 'AppModel', $this->plugin . '.Model'); + } + + $tables = array(); + $currentTables = $db->listSources(); + + $prefix = null; + if (isset($db->config['prefix'])) { + $prefix = $db->config['prefix']; + } + + if (!is_array($models) && $models !== false) { + if (isset($this->plugin)) { + $models = App::objects($this->plugin . '.Model', null, false); + } else { + $models = App::objects('Model'); + } + } + + if (is_array($models)) { + foreach ($models as $model) { + $importModel = $model; + $plugin = null; + if ($model == 'AppModel') { + continue; + } + + if (isset($this->plugin)) { + if ($model == $this->plugin . 'AppModel') { + continue; + } + $importModel = $model; + $plugin = $this->plugin . '.'; + } + + App::uses($importModel, $plugin . 'Model'); + if (!class_exists($importModel)) { + continue; + } + + $vars = get_class_vars($model); + if (empty($vars['useDbConfig']) || $vars['useDbConfig'] != $connection) { + continue; + } + + $Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection)); + $db = $Object->getDataSource(); + if (is_object($Object) && $Object->useTable !== false) { + $fulltable = $table = $db->fullTableName($Object, false); + if ($prefix && strpos($table, $prefix) !== 0) { + continue; + } + $table = $this->_noPrefixTable($prefix, $table); + + if (in_array($fulltable, $currentTables)) { + $key = array_search($fulltable, $currentTables); + if (empty($tables[$table])) { + $tables[$table] = $this->_columns($Object); + $tables[$table]['indexes'] = $db->index($Object); + $tables[$table]['tableParameters'] = $db->readTableParameters($fulltable); + unset($currentTables[$key]); + } + if (!empty($Object->hasAndBelongsToMany)) { + foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) { + if (isset($assocData['with'])) { + $class = $assocData['with']; + } + if (is_object($Object->$class)) { + $withTable = $db->fullTableName($Object->$class, false); + if ($prefix && strpos($withTable, $prefix) !== 0) { + continue; + } + if (in_array($withTable, $currentTables)) { + $key = array_search($withTable, $currentTables); + $noPrefixWith = $this->_noPrefixTable($prefix, $withTable); + + $tables[$noPrefixWith] = $this->_columns($Object->$class); + $tables[$noPrefixWith]['indexes'] = $db->index($Object->$class); + $tables[$noPrefixWith]['tableParameters'] = $db->readTableParameters($withTable); + unset($currentTables[$key]); + } + } + } + } + } + } + } + } + + if (!empty($currentTables)) { + foreach ($currentTables as $table) { + if ($prefix) { + if (strpos($table, $prefix) !== 0) { + continue; + } + $table = $this->_noPrefixTable($prefix, $table); + } + $Object = new AppModel(array( + 'name' => Inflector::classify($table), 'table' => $table, 'ds' => $connection + )); + + $systemTables = array( + 'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n' + ); + if (in_array($table, $systemTables)) { + $tables[$Object->table] = $this->_columns($Object); + $tables[$Object->table]['indexes'] = $db->index($Object); + $tables[$Object->table]['tableParameters'] = $db->readTableParameters($table); + } elseif ($models === false) { + $tables[$table] = $this->_columns($Object); + $tables[$table]['indexes'] = $db->index($Object); + $tables[$table]['tableParameters'] = $db->readTableParameters($table); + } else { + $tables['missing'][$table] = $this->_columns($Object); + $tables['missing'][$table]['indexes'] = $db->index($Object); + $tables['missing'][$table]['tableParameters'] = $db->readTableParameters($table); + } + } + } + + ksort($tables); + return compact('name', 'tables'); + } + +/** + * Writes schema file from object or options + * + * @param mixed $object schema object or options array + * @param array $options schema object properties to override object + * @return mixed false or string written to file + */ + public function write($object, $options = array()) { + if (is_object($object)) { + $object = get_object_vars($object); + $this->build($object); + } + + if (is_array($object)) { + $options = $object; + unset($object); + } + + extract(array_merge( + get_object_vars($this), $options + )); + + $out = "class {$name}Schema extends CakeSchema {\n"; + + if ($path !== $this->path) { + $out .= "\tvar \$path = '{$path}';\n\n"; + } + + if ($file !== $this->file) { + $out .= "\tvar \$file = '{$file}';\n\n"; + } + + if ($connection !== 'default') { + $out .= "\tvar \$connection = '{$connection}';\n\n"; + } + + $out .= "\tfunction before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tfunction after(\$event = array()) {\n\t}\n\n"; + + if (empty($tables)) { + $this->read(); + } + + foreach ($tables as $table => $fields) { + if (!is_numeric($table) && $table !== 'missing') { + $out .= $this->generateTable($table, $fields); + } + } + $out .= "}\n"; + + $file = new SplFileObject($path . DS . $file, 'w+'); + $content = ""; + if ($file->fwrite($content)) { + return $content; + } + return false; + } + +/** + * Generate the code for a table. Takes a table name and $fields array + * Returns a completed variable declaration to be used in schema classes + * + * @param string $table Table name you want returned. + * @param array $fields Array of field information to generate the table with. + * @return string Variable declaration for a schema class + */ + public function generateTable($table, $fields) { + $out = "\tvar \${$table} = array(\n"; + if (is_array($fields)) { + $cols = array(); + foreach ($fields as $field => $value) { + if ($field != 'indexes' && $field != 'tableParameters') { + if (is_string($value)) { + $type = $value; + $value = array('type'=> $type); + } + $col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', "; + unset($value['type']); + $col .= join(', ', $this->_values($value)); + } elseif ($field == 'indexes') { + $col = "\t\t'indexes' => array("; + $props = array(); + foreach ((array)$value as $key => $index) { + $props[] = "'{$key}' => array(" . join(', ', $this->_values($index)) . ")"; + } + $col .= join(', ', $props); + } elseif ($field == 'tableParameters') { + //@todo add charset, collate and engine here + $col = "\t\t'tableParameters' => array("; + $props = array(); + foreach ((array)$value as $key => $param) { + $props[] = "'{$key}' => '$param'"; + } + $col .= join(', ', $props); + } + $col .= ")"; + $cols[] = $col; + } + $out .= join(",\n", $cols); + } + $out .= "\n\t);\n"; + return $out; + } + +/** + * Compares two sets of schemas + * + * @param mixed $old Schema object or array + * @param mixed $new Schema object or array + * @return array Tables (that are added, dropped, or changed) + */ + public function compare($old, $new = null) { + if (empty($new)) { + $new = $this; + } + if (is_array($new)) { + if (isset($new['tables'])) { + $new = $new['tables']; + } + } else { + $new = $new->tables; + } + + if (is_array($old)) { + if (isset($old['tables'])) { + $old = $old['tables']; + } + } else { + $old = $old->tables; + } + $tables = array(); + foreach ($new as $table => $fields) { + if ($table == 'missing') { + continue; + } + if (!array_key_exists($table, $old)) { + $tables[$table]['add'] = $fields; + } else { + $diff = $this->_arrayDiffAssoc($fields, $old[$table]); + if (!empty($diff)) { + $tables[$table]['add'] = $diff; + } + $diff = $this->_arrayDiffAssoc($old[$table], $fields); + if (!empty($diff)) { + $tables[$table]['drop'] = $diff; + } + } + + foreach ($fields as $field => $value) { + if (!empty($old[$table][$field])) { + $diff = $this->_arrayDiffAssoc($value, $old[$table][$field]); + if (!empty($diff) && $field !== 'indexes' && $field !== 'tableParameters') { + $tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff); + } + } + + if (isset($add[$table][$field])) { + $wrapper = array_keys($fields); + if ($column = array_search($field, $wrapper)) { + if (isset($wrapper[$column - 1])) { + $tables[$table]['add'][$field]['after'] = $wrapper[$column - 1]; + } + } + } + } + + if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) { + $diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']); + if ($diff) { + if (!isset($tables[$table])) { + $tables[$table] = array(); + } + if (isset($diff['drop'])) { + $tables[$table]['drop']['indexes'] = $diff['drop']; + } + if ($diff && isset($diff['add'])) { + $tables[$table]['add']['indexes'] = $diff['add']; + } + } + } + if (isset($old[$table]['tableParameters']) && isset($new[$table]['tableParameters'])) { + $diff = $this->_compareTableParameters($new[$table]['tableParameters'], $old[$table]['tableParameters']); + if ($diff) { + $tables[$table]['change']['tableParameters'] = $diff; + } + } + } + return $tables; + } + +/** + * Extended array_diff_assoc noticing change from/to NULL values + * + * It behaves almost the same way as array_diff_assoc except for NULL values: if + * one of the values is not NULL - change is detected. It is useful in situation + * where one value is strval('') ant other is strval(null) - in string comparing + * methods this results as EQUAL, while it is not. + * + * @param array $array1 Base array + * @param array $array2 Corresponding array checked for equality + * @return array Difference as array with array(keys => values) from input array + * where match was not found. + */ + protected function _arrayDiffAssoc($array1, $array2) { + $difference = array(); + foreach ($array1 as $key => $value) { + if (!array_key_exists($key, $array2)) { + $difference[$key] = $value; + continue; + } + $correspondingValue = $array2[$key]; + if (is_null($value) !== is_null($correspondingValue)) { + $difference[$key] = $value; + continue; + } + if (is_bool($value) !== is_bool($correspondingValue)) { + $difference[$key] = $value; + continue; + } + $compare = strval($value); + $correspondingValue = strval($correspondingValue); + if ($compare === $correspondingValue) { + continue; + } + $difference[$key] = $value; + } + return $difference; + } + +/** + * Formats Schema columns from Model Object + * + * @param array $values options keys(type, null, default, key, length, extra) + * @return array Formatted values + */ + protected function _values($values) { + $vals = array(); + if (is_array($values)) { + foreach ($values as $key => $val) { + if (is_array($val)) { + $vals[] = "'{$key}' => array('" . implode("', '", $val) . "')"; + } else if (!is_numeric($key)) { + $val = var_export($val, true); + $vals[] = "'{$key}' => {$val}"; + } + } + } + return $vals; + } + +/** + * Formats Schema columns from Model Object + * + * @param array $Obj model object + * @return array Formatted columns + */ + protected function _columns(&$Obj) { + $db = $Obj->getDataSource(); + $fields = $Obj->schema(true); + + $columns = $props = array(); + foreach ($fields as $name => $value) { + if ($Obj->primaryKey == $name) { + $value['key'] = 'primary'; + } + if (!isset($db->columns[$value['type']])) { + trigger_error(__d('cake_dev', 'Schema generation error: invalid column type %s does not exist in DBO', $value['type']), E_USER_NOTICE); + continue; + } else { + $defaultCol = $db->columns[$value['type']]; + if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) { + unset($value['length']); + } elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) { + unset($value['length']); + } + unset($value['limit']); + } + + if (isset($value['default']) && ($value['default'] === '' || $value['default'] === false)) { + unset($value['default']); + } + if (empty($value['length'])) { + unset($value['length']); + } + if (empty($value['key'])) { + unset($value['key']); + } + $columns[$name] = $value; + } + + return $columns; + } + +/** + * Compare two schema files table Parameters + * + * @param array $new New indexes + * @param array $old Old indexes + * @return mixed False on failure, or an array of parameters to add & drop. + */ + protected function _compareTableParameters($new, $old) { + if (!is_array($new) || !is_array($old)) { + return false; + } + $change = $this->_arrayDiffAssoc($new, $old); + return $change; + } + +/** + * Compare two schema indexes + * + * @param array $new New indexes + * @param array $old Old indexes + * @return mixed false on failure or array of indexes to add and drop + */ + protected function _compareIndexes($new, $old) { + if (!is_array($new) || !is_array($old)) { + return false; + } + + $add = $drop = array(); + + $diff = $this->_arrayDiffAssoc($new, $old); + if (!empty($diff)) { + $add = $diff; + } + + $diff = $this->_arrayDiffAssoc($old, $new); + if (!empty($diff)) { + $drop = $diff; + } + + foreach ($new as $name => $value) { + if (isset($old[$name])) { + $newUnique = isset($value['unique']) ? $value['unique'] : 0; + $oldUnique = isset($old[$name]['unique']) ? $old[$name]['unique'] : 0; + $newColumn = $value['column']; + $oldColumn = $old[$name]['column']; + + $diff = false; + + if ($newUnique != $oldUnique) { + $diff = true; + } elseif (is_array($newColumn) && is_array($oldColumn)) { + $diff = ($newColumn !== $oldColumn); + } elseif (is_string($newColumn) && is_string($oldColumn)) { + $diff = ($newColumn != $oldColumn); + } else { + $diff = true; + } + if ($diff) { + $drop[$name] = null; + $add[$name] = $value; + } + } + } + return array_filter(compact('add', 'drop')); + } + +/** + * Trim the table prefix from the full table name, and return the prefix-less table + * + * @param string $prefix Table prefix + * @param string $table Full table name + * @return string Prefix-less table name + */ + protected function _noPrefixTable($prefix, $table) { + return preg_replace('/^' . preg_quote($prefix) . '/', '', $table); + } +} diff --git a/app/Cake/Model/ConnectionManager.php b/app/Cake/Model/ConnectionManager.php new file mode 100644 index 00000000..8e5ef8d7 --- /dev/null +++ b/app/Cake/Model/ConnectionManager.php @@ -0,0 +1,271 @@ +{$name}); + self::$_dataSources[$name]->configKeyName = $name; + + return self::$_dataSources[$name]; + } + +/** + * Gets the list of available DataSource connections + * This will only return the datasources instantiated by this manager + * It differs from enumConnectionObjects, since the latter will return all configured connections + * + * @return array List of available connections + */ + public static function sourceList() { + if (empty(self::$_init)) { + self::_init(); + } + return array_keys(self::$_dataSources); + } + +/** + * Gets a DataSource name from an object reference. + * + * @param DataSource $source DataSource object + * @return string Datasource name, or null if source is not present + * in the ConnectionManager. + */ + public static function getSourceName($source) { + if (empty(self::$_init)) { + self::_init(); + } + foreach (self::$_dataSources as $name => $ds) { + if ($ds === $source) { + return $name; + } + } + return null; + } + +/** + * Loads the DataSource class for the given connection name + * + * @param mixed $connName A string name of the connection, as defined in app/Config/database.php, + * or an array containing the filename (without extension) and class name of the object, + * to be found in app/Model/Datasource/ or lib/Cake/Model/Datasource/. + * @return boolean True on success, null on failure or false if the class is already loaded + * @throws MissingDatasourceFileException + */ + public static function loadDataSource($connName) { + if (empty(self::$_init)) { + self::_init(); + } + + if (is_array($connName)) { + $conn = $connName; + } else { + $conn = self::$_connectionsEnum[$connName]; + } + + if (class_exists($conn['classname'], false)) { + return false; + } + + $plugin = $package = null; + if (!empty($conn['plugin'])) { + $plugin = $conn['plugin'] . '.'; + } + if (!empty($conn['package'])) { + $package = '/' . $conn['package']; + } + + App::uses($conn['classname'], $plugin . 'Model/Datasource' . $package); + if (!class_exists($conn['classname'])) { + throw new MissingDatasourceFileException(array('class' => $conn['classname'], 'plugin' => $plugin)); + } + return true; + } + +/** + * Return a list of connections + * + * @return array An associative array of elements where the key is the connection name + * (as defined in Connections), and the value is an array with keys 'filename' and 'classname'. + */ + public static function enumConnectionObjects() { + if (empty(self::$_init)) { + self::_init(); + } + return (array) self::$config; + } + +/** + * Dynamically creates a DataSource object at runtime, with the given name and settings + * + * @param string $name The DataSource name + * @param array $config The DataSource configuration settings + * @return DataSource A reference to the DataSource object, or null if creation failed + */ + public static function create($name = '', $config = array()) { + if (empty(self::$_init)) { + self::_init(); + } + + if (empty($name) || empty($config) || array_key_exists($name, self::$_connectionsEnum)) { + return null; + } + self::$config->{$name} = $config; + self::$_connectionsEnum[$name] = self::_connectionData($config); + $return = self::getDataSource($name); + return $return; + } + +/** + * Removes a connection configuration at runtime given its name + * + * @param string $name the connection name as it was created + * @return boolean success if connection was removed, false if it does not exist + */ + public static function drop($name) { + if (empty(self::$_init)) { + self::_init(); + } + + if (!isset(self::$config->{$name})) { + return false; + } + unset(self::$_connectionsEnum[$name], self::$_dataSources[$name], self::$config->{$name}); + return true; + } + +/** + * Gets a list of class and file names associated with the user-defined DataSource connections + * + * @param string $name Connection name + * @return void + * @throws MissingDatasourceConfigException + */ + protected static function _getConnectionObject($name) { + if (!empty(self::$config->{$name})) { + self::$_connectionsEnum[$name] = self::_connectionData(self::$config->{$name}); + } else { + throw new MissingDatasourceConfigException(array('config' => $name)); + } + } + +/** + * Returns the file, class name, and parent for the given driver. + * + * @param array $config Array with connection configuration. Key 'datasource' is required + * @return array An indexed array with: filename, classname, plugin and parent + */ + protected static function _connectionData($config) { + $package = $classname = $plugin = null; + + list($plugin, $classname) = pluginSplit($config['datasource']); + if (strpos($classname, '/') !== false) { + $package = dirname($classname); + $classname = basename($classname); + } + return compact('package', 'classname', 'plugin'); + } + +/** + * Destructor. + * + * @return void + */ + public static function shutdown() { + if (Configure::read('Session.defaults') == 'database' && function_exists('session_write_close')) { + session_write_close(); + } + } +} diff --git a/app/Cake/Model/Datasource/CakeSession.php b/app/Cake/Model/Datasource/CakeSession.php new file mode 100644 index 00000000..b8641c33 --- /dev/null +++ b/app/Cake/Model/Datasource/CakeSession.php @@ -0,0 +1,752 @@ + values + * @param array $new New set of variable => value + * @return void + */ + protected static function _overwrite(&$old, $new) { + if (!empty($old)) { + foreach ($old as $key => $var) { + if (!isset($new[$key])) { + unset($old[$key]); + } + } + } + foreach ($new as $key => $var) { + $old[$key] = $var; + } + } + +/** + * Return error description for given error number. + * + * @param integer $errorNumber Error to set + * @return string Error as string + */ + protected static function _error($errorNumber) { + if (!is_array(self::$error) || !array_key_exists($errorNumber, self::$error)) { + return false; + } else { + return self::$error[$errorNumber]; + } + } + +/** + * Returns last occurred error as a string, if any. + * + * @return mixed Error description as a string, or false. + */ + public static function error() { + if (self::$lastError) { + return self::_error(self::$lastError); + } + return false; + } + +/** + * Returns true if session is valid. + * + * @return boolean Success + */ + public static function valid() { + if (self::read('Config')) { + if (self::_validAgentAndTime() && self::$error === false) { + self::$valid = true; + } else { + self::$valid = false; + self::_setError(1, 'Session Highjacking Attempted !!!'); + } + } + return self::$valid; + } + +/** + * Tests that the user agent is valid and that the session hasn't 'timed out'. + * Since timeouts are implemented in CakeSession it checks the current self::$time + * against the time the session is set to expire. The User agent is only checked + * if Session.checkAgent == true. + * + * @return boolean + */ + protected static function _validAgentAndTime() { + $config = self::read('Config'); + $validAgent = ( + Configure::read('Session.checkAgent') === false || + self::$_userAgent == $config['userAgent'] + ); + return ($validAgent && self::$time <= $config['time']); + } + +/** + * Get / Set the userAgent + * + * @param string $userAgent Set the userAgent + * @return void + */ + public static function userAgent($userAgent = null) { + if ($userAgent) { + self::$_userAgent = $userAgent; + } + return self::$_userAgent; + } + +/** + * Returns given session variable, or all of them, if no parameters given. + * + * @param mixed $name The name of the session variable (or a path as sent to Set.extract) + * @return mixed The value of the session variable + */ + public static function read($name = null) { + if (!self::started() && !self::start()) { + return false; + } + if (is_null($name)) { + return self::_returnSessionVars(); + } + if (empty($name)) { + return false; + } + $result = Set::classicExtract($_SESSION, $name); + + if (!is_null($result)) { + return $result; + } + self::_setError(2, "$name doesn't exist"); + return null; + } + +/** + * Returns all session variables. + * + * @return mixed Full $_SESSION array, or false on error. + */ + protected static function _returnSessionVars() { + if (!empty($_SESSION)) { + return $_SESSION; + } + self::_setError(2, 'No Session vars set'); + return false; + } + +/** + * Writes value to given session variable name. + * + * @param mixed $name Name of variable + * @param string $value Value to write + * @return boolean True if the write was successful, false if the write failed + */ + public static function write($name, $value = null) { + if (!self::started() && !self::start()) { + return false; + } + if (empty($name)) { + return false; + } + $write = $name; + if (!is_array($name)) { + $write = array($name => $value); + } + foreach ($write as $key => $val) { + self::_overwrite($_SESSION, Set::insert($_SESSION, $key, $val)); + if (Set::classicExtract($_SESSION, $key) !== $val) { + return false; + } + } + return true; + } + +/** + * Helper method to destroy invalid sessions. + * + * @return void + */ + public static function destroy() { + if (self::started()) { + session_destroy(); + } + self::clear(); + } + +/** + * Clears the session, the session id, and renew's the session. + * + * @return void + */ + public static function clear() { + $_SESSION = null; + self::$id = null; + self::start(); + self::renew(); + } + +/** + * Helper method to initialize a session, based on Cake core settings. + * + * Sessions can be configured with a few shortcut names as well as have any number of ini settings declared. + * + * @return void + * @throws CakeSessionException Throws exceptions when ini_set() fails. + */ + protected static function _configureSession() { + $sessionConfig = Configure::read('Session'); + $iniSet = function_exists('ini_set'); + + if (isset($sessionConfig['defaults'])) { + $defaults = self::_defaultConfig($sessionConfig['defaults']); + if ($defaults) { + $sessionConfig = Set::merge($defaults, $sessionConfig); + } + } + if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) { + $sessionConfig['ini']['session.cookie_secure'] = 1; + } + if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) { + $sessionConfig['cookieTimeout'] = $sessionConfig['timeout']; + } + if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) { + $sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60; + } + if (!isset($sessionConfig['ini']['session.name'])) { + $sessionConfig['ini']['session.name'] = $sessionConfig['cookie']; + } + if (!empty($sessionConfig['handler'])) { + $sessionConfig['ini']['session.save_handler'] = 'user'; + } + + if (empty($_SESSION)) { + if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) { + foreach ($sessionConfig['ini'] as $setting => $value) { + if (ini_set($setting, $value) === false) { + throw new CakeSessionException(sprintf( + __d('cake_dev', 'Unable to configure the session, setting %s failed.'), + $setting + )); + } + } + } + } + if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) { + call_user_func_array('session_set_save_handler', $sessionConfig['handler']); + } + if (!empty($sessionConfig['handler']['engine'])) { + $handler = self::_getHandler($sessionConfig['handler']['engine']); + session_set_save_handler( + array($handler, 'open'), + array($handler, 'close'), + array($handler, 'read'), + array($handler, 'write'), + array($handler, 'destroy'), + array($handler, 'gc') + ); + } + Configure::write('Session', $sessionConfig); + self::$sessionTime = self::$time + ($sessionConfig['timeout'] * 60); + } + +/** + * Find the handler class and make sure it implements the correct interface. + * + * @param string $handler + * @return void + * @throws CakeSessionException + */ + protected static function _getHandler($handler) { + list($plugin, $class) = pluginSplit($handler, true); + App::uses($class, $plugin . 'Model/Datasource/Session'); + if (!class_exists($class)) { + throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class)); + } + $handler = new $class(); + if ($handler instanceof CakeSessionHandlerInterface) { + return $handler; + } + throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.')); + } + +/** + * Get one of the prebaked default session configurations. + * + * @param string $name + * @return boolean|array + */ + protected static function _defaultConfig($name) { + $defaults = array( + 'php' => array( + 'cookie' => 'CAKEPHP', + 'timeout' => 240, + 'cookieTimeout' => 240, + 'ini' => array( + 'session.use_trans_sid' => 0, + 'session.cookie_path' => self::$path, + 'session.save_handler' => 'files' + ) + ), + 'cake' => array( + 'cookie' => 'CAKEPHP', + 'timeout' => 240, + 'cookieTimeout' => 240, + 'ini' => array( + 'session.use_trans_sid' => 0, + 'url_rewriter.tags' => '', + 'session.serialize_handler' => 'php', + 'session.use_cookies' => 1, + 'session.cookie_path' => self::$path, + 'session.auto_start' => 0, + 'session.save_path' => TMP . 'sessions', + 'session.save_handler' => 'files' + ) + ), + 'cache' => array( + 'cookie' => 'CAKEPHP', + 'timeout' => 240, + 'cookieTimeout' => 240, + 'ini' => array( + 'session.use_trans_sid' => 0, + 'url_rewriter.tags' => '', + 'session.auto_start' => 0, + 'session.use_cookies' => 1, + 'session.cookie_path' => self::$path, + 'session.save_handler' => 'user', + ), + 'handler' => array( + 'engine' => 'CacheSession', + 'config' => 'default' + ) + ), + 'database' => array( + 'cookie' => 'CAKEPHP', + 'timeout' => 240, + 'cookieTimeout' => 240, + 'ini' => array( + 'session.use_trans_sid' => 0, + 'url_rewriter.tags' => '', + 'session.auto_start' => 0, + 'session.use_cookies' => 1, + 'session.cookie_path' => self::$path, + 'session.save_handler' => 'user', + 'session.serialize_handler' => 'php', + ), + 'handler' => array( + 'engine' => 'DatabaseSession', + 'model' => 'Session' + ) + ) + ); + if (isset($defaults[$name])) { + return $defaults[$name]; + } + return false; + } + +/** + * Helper method to start a session + * + * @return boolean Success + */ + protected static function _startSession() { + if (headers_sent()) { + if (empty($_SESSION)) { + $_SESSION = array(); + } + } elseif (!isset($_SESSION)) { + session_cache_limiter ("must-revalidate"); + session_start(); + header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"'); + } else { + session_start(); + } + return true; + } + +/** + * Helper method to create a new session. + * + * @return void + */ + protected static function _checkValid() { + if (!self::started() && !self::start()) { + self::$valid = false; + return false; + } + if ($config = self::read('Config')) { + $sessionConfig = Configure::read('Session'); + + if (self::_validAgentAndTime()) { + $time = $config['time']; + self::write('Config.time', self::$sessionTime); + if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) { + $check = $config['countdown']; + $check -= 1; + self::write('Config.countdown', $check); + + if (time() > ($time - ($sessionConfig['timeout'] * 60) + 2) || $check < 1) { + self::renew(); + self::write('Config.countdown', self::$requestCountdown); + } + } + self::$valid = true; + } else { + self::destroy(); + self::$valid = false; + self::_setError(1, 'Session Highjacking Attempted !!!'); + } + } else { + self::write('Config.userAgent', self::$_userAgent); + self::write('Config.time', self::$sessionTime); + self::write('Config.countdown', self::$requestCountdown); + self::$valid = true; + } + } + +/** + * Restarts this session. + * + * @return void + */ + public static function renew() { + if (session_id()) { + if (session_id() != '' || isset($_COOKIE[session_name()])) { + setcookie(Configure::read('Session.cookie'), '', time() - 42000, self::$path); + } + session_regenerate_id(true); + } + } + +/** + * Helper method to set an internal error message. + * + * @param integer $errorNumber Number of the error + * @param string $errorMessage Description of the error + * @return void + */ + protected static function _setError($errorNumber, $errorMessage) { + if (self::$error === false) { + self::$error = array(); + } + self::$error[$errorNumber] = $errorMessage; + self::$lastError = $errorNumber; + } +} + + +/** + * Interface for Session handlers. Custom session handler classes should implement + * this interface as it allows CakeSession know how to map methods to session_set_save_handler() + * + * @package Cake.Model.Datasource + */ +interface CakeSessionHandlerInterface { +/** + * Method called on open of a session. + * + * @return boolean Success + */ + public function open(); + +/** + * Method called on close of a session. + * + * @return boolean Success + */ + public function close(); + +/** + * Method used to read from a session. + * + * @param mixed $id The key of the value to read + * @return mixed The value of the key or false if it does not exist + */ + public function read($id); + +/** + * Helper function called on write for sessions. + * + * @param integer $id ID that uniquely identifies session in database + * @param mixed $data The value of the data to be saved. + * @return boolean True for successful write, false otherwise. + */ + public function write($id, $data); + +/** + * Method called on the destruction of a session. + * + * @param integer $id ID that uniquely identifies session in database + * @return boolean True for successful delete, false otherwise. + */ + public function destroy($id); + +/** + * Run the Garbage collection on the session storage. This method should vacuum all + * expired or dead sessions. + * + * @param integer $expires Timestamp (defaults to current time) + * @return boolean Success + */ + public function gc($expires = null); +} + + +// Initialize the session +CakeSession::init(); diff --git a/app/Cake/Model/Datasource/DataSource.php b/app/Cake/Model/Datasource/DataSource.php new file mode 100644 index 00000000..34d7a492 --- /dev/null +++ b/app/Cake/Model/Datasource/DataSource.php @@ -0,0 +1,423 @@ +setConfig($config); + } + +/** + * Caches/returns cached results for child instances + * + * @param mixed $data + * @return array Array of sources available in this datasource. + */ + public function listSources($data = null) { + if ($this->cacheSources === false) { + return null; + } + + if ($this->_sources !== null) { + return $this->_sources; + } + + $key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list'; + $key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key); + $sources = Cache::read($key, '_cake_model_'); + + if (empty($sources)) { + $sources = $data; + Cache::write($key, $data, '_cake_model_'); + } + + return $this->_sources = $sources; + } + +/** + * Returns a Model description (metadata) or null if none found. + * + * @param Model|string $model + * @return array Array of Metadata for the $model + */ + public function describe($model) { + if ($this->cacheSources === false) { + return null; + } + if (is_string($model)) { + $table = $model; + } else { + $table = $model->tablePrefix . $model->table; + } + + if (isset($this->_descriptions[$table])) { + return $this->_descriptions[$table]; + } + $cache = $this->_cacheDescription($table); + + if ($cache !== null) { + $this->_descriptions[$table] =& $cache; + return $cache; + } + return null; + } + +/** + * Begin a transaction + * + * @return boolean Returns true if a transaction is not in progress + */ + public function begin() { + return !$this->_transactionStarted; + } + +/** + * Commit a transaction + * + * @return boolean Returns true if a transaction is in progress + */ + public function commit() { + return $this->_transactionStarted; + } + +/** + * Rollback a transaction + * + * @return boolean Returns true if a transaction is in progress + */ + public function rollback() { + return $this->_transactionStarted; + } + +/** + * Converts column types to basic types + * + * @param string $real Real column type (i.e. "varchar(255)") + * @return string Abstract column type (i.e. "string") + */ + public function column($real) { + return false; + } + +/** + * Used to create new records. The "C" CRUD. + * + * To-be-overridden in subclasses. + * + * @param Model $model The Model to be created. + * @param array $fields An Array of fields to be saved. + * @param array $values An Array of values to save. + * @return boolean success + */ + public function create(Model $model, $fields = null, $values = null) { + return false; + } + +/** + * Used to read records from the Datasource. The "R" in CRUD + * + * To-be-overridden in subclasses. + * + * @param Model $model The model being read. + * @param array $queryData An array of query data used to find the data you want + * @return mixed + */ + public function read(Model $model, $queryData = array()) { + return false; + } + +/** + * Update a record(s) in the datasource. + * + * To-be-overridden in subclasses. + * + * @param Model $model Instance of the model class being updated + * @param array $fields Array of fields to be updated + * @param array $values Array of values to be update $fields to. + * @return boolean Success + */ + public function update(Model $model, $fields = null, $values = null) { + return false; + } + +/** + * Delete a record(s) in the datasource. + * + * To-be-overridden in subclasses. + * + * @param Model $model The model class having record(s) deleted + * @param mixed $id Primary key of the model + * @return void + */ + public function delete(Model $model, $id = null) { + if ($id == null) { + $id = $model->id; + } + } + +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param mixed $source + * @return mixed Last ID key generated in previous INSERT + */ + public function lastInsertId($source = null) { + return false; + } + +/** + * Returns the number of rows returned by last operation. + * + * @param mixed $source + * @return integer Number of rows returned by last operation + */ + public function lastNumRows($source = null) { + return false; + } + +/** + * Returns the number of rows affected by last query. + * + * @param mixed $source + * @return integer Number of rows affected by last query. + */ + public function lastAffected($source = null) { + return false; + } + +/** + * Check whether the conditions for the Datasource being available + * are satisfied. Often used from connect() to check for support + * before establishing a connection. + * + * @return boolean Whether or not the Datasources conditions for use are met. + */ + public function enabled() { + return true; + } + +/** + * Sets the configuration for the DataSource. + * Merges the $config information with the _baseConfig and the existing $config property. + * + * @param array $config The configuration array + * @return void + */ + public function setConfig($config = array()) { + $this->config = array_merge($this->_baseConfig, $this->config, $config); + } + +/** + * Cache the DataSource description + * + * @param string $object The name of the object (model) to cache + * @param mixed $data The description of the model, usually a string or array + * @return mixed + */ + protected function _cacheDescription($object, $data = null) { + if ($this->cacheSources === false) { + return null; + } + + if ($data !== null) { + $this->_descriptions[$object] =& $data; + } + + $key = ConnectionManager::getSourceName($this) . '_' . $object; + $cache = Cache::read($key, '_cake_model_'); + + if (empty($cache)) { + $cache = $data; + Cache::write($key, $cache, '_cake_model_'); + } + + return $cache; + } + +/** + * Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data. + * + * @param string $query Query string needing replacements done. + * @param array $data Array of data with values that will be inserted in placeholders. + * @param string $association Name of association model being replaced + * @param array $assocData + * @param Model $model Instance of the model to replace $__cakeID__$ + * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$ + * @param array $stack + * @return string String of query data with placeholders replaced. + * @todo Remove and refactor $assocData, ensure uses of the method have the param removed too. + */ + public function insertQueryData($query, $data, $association, $assocData, Model $model, Model $linkModel, $stack) { + $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}'); + + foreach ($keys as $key) { + $val = null; + $type = null; + + if (strpos($query, $key) !== false) { + switch ($key) { + case '{$__cakeID__$}': + if (isset($data[$model->alias]) || isset($data[$association])) { + if (isset($data[$model->alias][$model->primaryKey])) { + $val = $data[$model->alias][$model->primaryKey]; + } elseif (isset($data[$association][$model->primaryKey])) { + $val = $data[$association][$model->primaryKey]; + } + } else { + $found = false; + foreach (array_reverse($stack) as $assoc) { + if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) { + $val = $data[$assoc][$model->primaryKey]; + $found = true; + break; + } + } + if (!$found) { + $val = ''; + } + } + $type = $model->getColumnType($model->primaryKey); + break; + case '{$__cakeForeignKey__$}': + foreach ($model->associations() as $id => $name) { + foreach ($model->$name as $assocName => $assoc) { + if ($assocName === $association) { + if (isset($assoc['foreignKey'])) { + $foreignKey = $assoc['foreignKey']; + $assocModel = $model->$assocName; + $type = $assocModel->getColumnType($assocModel->primaryKey); + + if (isset($data[$model->alias][$foreignKey])) { + $val = $data[$model->alias][$foreignKey]; + } elseif (isset($data[$association][$foreignKey])) { + $val = $data[$association][$foreignKey]; + } else { + $found = false; + foreach (array_reverse($stack) as $assoc) { + if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) { + $val = $data[$assoc][$foreignKey]; + $found = true; + break; + } + } + if (!$found) { + $val = ''; + } + } + } + break 3; + } + } + } + break; + } + if (empty($val) && $val !== '0') { + return false; + } + $query = str_replace($key, $this->value($val, $type), $query); + } + } + return $query; + } + +/** + * To-be-overridden in subclasses. + * + * @param Model $model Model instance + * @param string $key Key name to make + * @return string Key name for model. + */ + public function resolveKey(Model $model, $key) { + return $model->alias . $key; + } + +/** + * Closes the current datasource. + * + */ + public function __destruct() { + if ($this->_transactionStarted) { + $this->rollback(); + } + if ($this->connected) { + $this->close(); + } + } +} diff --git a/app/Cake/Model/Datasource/Database/Mysql.php b/app/Cake/Model/Datasource/Database/Mysql.php new file mode 100644 index 00000000..787499eb --- /dev/null +++ b/app/Cake/Model/Datasource/Database/Mysql.php @@ -0,0 +1,680 @@ + true, + 'host' => 'localhost', + 'login' => 'root', + 'password' => '', + 'database' => 'cake', + 'port' => '3306' + ); + +/** + * Reference to the PDO object connection + * + * @var PDO $_connection + */ + protected $_connection = null; + +/** + * Start quote + * + * @var string + */ + public $startQuote = "`"; + +/** + * End quote + * + * @var string + */ + public $endQuote = "`"; + +/** + * use alias for update and delete. Set to true if version >= 4.1 + * + * @var boolean + */ + protected $_useAlias = true; + +/** + * Index of basic SQL commands + * + * @var array + */ + protected $_commands = array( + 'begin' => 'START TRANSACTION', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); + +/** + * List of engine specific additional field parameters used on table creating + * + * @var array + */ + public $fieldParameters = array( + 'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'), + 'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'), + 'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault') + ); + +/** + * List of table engine specific parameters used on table creating + * + * @var array + */ + public $tableParameters = array( + 'charset' => array('value' => 'DEFAULT CHARSET', 'quote' => false, 'join' => '=', 'column' => 'charset'), + 'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => '=', 'column' => 'Collation'), + 'engine' => array('value' => 'ENGINE', 'quote' => false, 'join' => '=', 'column' => 'Engine') + ); + +/** + * MySQL column definition + * + * @var array + */ + public $columns = array( + 'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'), + 'string' => array('name' => 'varchar', 'limit' => '255'), + 'text' => array('name' => 'text'), + 'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'), + 'float' => array('name' => 'float', 'formatter' => 'floatval'), + 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'), + 'binary' => array('name' => 'blob'), + 'boolean' => array('name' => 'tinyint', 'limit' => '1') + ); + +/** + * Connects to the database using options in the given configuration array. + * + * @return boolean True if the database could be connected, else false + * @throws MissingConnectionException + */ + public function connect() { + $config = $this->config; + $this->connected = false; + try { + $flags = array( + PDO::ATTR_PERSISTENT => $config['persistent'], + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + if (!empty($config['encoding'])) { + $flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['encoding']; + } + $this->_connection = new PDO( + "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}", + $config['login'], + $config['password'], + $flags + ); + $this->connected = true; + } catch (PDOException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); + } + + $this->_useAlias = (bool)version_compare($this->getVersion(), "4.1", ">="); + + return $this->connected; + } + +/** + * Check whether the MySQL extension is installed/loaded + * + * @return boolean + */ + public function enabled() { + return in_array('mysql', PDO::getAvailableDrivers()); + } + +/** + * Returns an array of sources (tables) in the database. + * + * @param mixed $data + * @return array Array of tablenames in the database + */ + public function listSources($data = null) { + $cache = parent::listSources(); + if ($cache != null) { + return $cache; + } + $result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database'])); + + if (!$result) { + $result->closeCursor(); + return array(); + } else { + $tables = array(); + + while ($line = $result->fetch()) { + $tables[] = $line[0]; + } + + $result->closeCursor(); + parent::listSources($tables); + return $tables; + } + } + +/** + * Builds a map of the columns contained in a result + * + * @param PDOStatement $results + * @return void + */ + public function resultSet($results) { + $this->map = array(); + $numFields = $results->columnCount(); + $index = 0; + + while ($numFields-- > 0) { + $column = $results->getColumnMeta($index); + if (empty($column['native_type'])) { + $type = ($column['len'] == 1) ? 'boolean' : 'string'; + } else { + $type = $column['native_type']; + } + if (!empty($column['table']) && strpos($column['name'], $this->virtualFieldSeparator) === false) { + $this->map[$index++] = array($column['table'], $column['name'], $type); + } else { + $this->map[$index++] = array(0, $column['name'], $type); + } + } + } + +/** + * Fetches the next row from the current result set + * + * @return mixed array with results fetched and mapped to column names or false if there is no results left to fetch + */ + public function fetchResult() { + if ($row = $this->_result->fetch()) { + $resultRow = array(); + foreach ($this->map as $col => $meta) { + list($table, $column, $type) = $meta; + $resultRow[$table][$column] = $row[$col]; + if ($type === 'boolean' && $row[$col] !== null) { + $resultRow[$table][$column] = $this->boolean($resultRow[$table][$column]); + } + } + return $resultRow; + } + $this->_result->closeCursor(); + return false; + } + +/** + * Gets the database encoding + * + * @return string The database encoding + */ + public function getEncoding() { + return $this->_execute('SHOW VARIABLES LIKE ?', array('character_set_client'))->fetchObject()->Value; + } + +/** + * Gets the version string of the database server + * + * @return string The database encoding + */ + public function getVersion() { + return $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + } + +/** + * Query charset by collation + * + * @param string $name Collation name + * @return string Character set name + */ + public function getCharsetName($name) { + if ((bool)version_compare($this->getVersion(), "5", ">=")) { + $r = $this->_execute('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME = ?', array($name)); + $cols = $r->fetch(); + + if (isset($cols['CHARACTER_SET_NAME'])) { + return $cols['CHARACTER_SET_NAME']; + } + } + return false; + } + +/** + * Returns an array of the fields in given table name. + * + * @param Model $model Name of database table to inspect or model instance + * @return array Fields in table. Keys are name and type + * @throws CakeException + */ + public function describe(Model $model) { + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + $fields = false; + $cols = $this->_execute('SHOW FULL COLUMNS FROM ' . $this->fullTableName($model)); + if (!$cols) { + throw new CakeException(__d('cake_dev', 'Could not describe table for %s', $model->name)); + } + + foreach ($cols as $column) { + $fields[$column->Field] = array( + 'type' => $this->column($column->Type), + 'null' => ($column->Null === 'YES' ? true : false), + 'default' => $column->Default, + 'length' => $this->length($column->Type), + ); + if (!empty($column->Key) && isset($this->index[$column->Key])) { + $fields[$column->Field]['key'] = $this->index[$column->Key]; + } + foreach ($this->fieldParameters as $name => $value) { + if (!empty($column->{$value['column']})) { + $fields[$column->Field][$name] = $column->{$value['column']}; + } + } + if (isset($fields[$column->Field]['collate'])) { + $charset = $this->getCharsetName($fields[$column->Field]['collate']); + if ($charset) { + $fields[$column->Field]['charset'] = $charset; + } + } + } + $this->_cacheDescription($this->fullTableName($model, false), $fields); + $cols->closeCursor(); + return $fields; + } + +/** + * Generates and executes an SQL UPDATE statement for given model, fields, and values. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return array + */ + public function update(Model $model, $fields = array(), $values = null, $conditions = null) { + if (!$this->_useAlias) { + return parent::update($model, $fields, $values, $conditions); + } + + if ($values == null) { + $combined = $fields; + } else { + $combined = array_combine($fields, $values); + } + + $alias = $joins = false; + $fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions)); + $fields = implode(', ', $fields); + $table = $this->fullTableName($model); + + if (!empty($conditions)) { + $alias = $this->name($model->alias); + if ($model->name == $model->alias) { + $joins = implode(' ', $this->_getJoins($model)); + } + } + $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model); + + if ($conditions === false) { + return false; + } + + if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) { + $model->onError(); + return false; + } + return true; + } + +/** + * Generates and executes an SQL DELETE statement for given id/conditions on given model. + * + * @param Model $model + * @param mixed $conditions + * @return boolean Success + */ + public function delete(Model $model, $conditions = null) { + if (!$this->_useAlias) { + return parent::delete($model, $conditions); + } + $alias = $this->name($model->alias); + $table = $this->fullTableName($model); + $joins = implode(' ', $this->_getJoins($model)); + + if (empty($conditions)) { + $alias = $joins = false; + } + $complexConditions = false; + foreach ((array)$conditions as $key => $value) { + if (strpos($key, $model->alias) === false) { + $complexConditions = true; + break; + } + } + if (!$complexConditions) { + $joins = false; + } + + $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model); + if ($conditions === false) { + return false; + } + if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) { + $model->onError(); + return false; + } + return true; + } + +/** + * Sets the database encoding + * + * @param string $enc Database encoding + * @return boolean + */ + public function setEncoding($enc) { + return $this->_execute('SET NAMES ' . $enc) !== false; + } + +/** + * Returns an array of the indexes in given datasource name. + * + * @param string $model Name of model to inspect + * @return array Fields in table. Keys are column and unique + */ + public function index($model) { + $index = array(); + $table = $this->fullTableName($model); + $old = version_compare($this->getVersion(), '4.1', '<='); + if ($table) { + $indices = $this->_execute('SHOW INDEX FROM ' . $table); + while ($idx = $indices->fetch()) { + if ($old) { + $idx = (object) current((array)$idx); + } + if (!isset($index[$idx->Key_name]['column'])) { + $col = array(); + $index[$idx->Key_name]['column'] = $idx->Column_name; + $index[$idx->Key_name]['unique'] = intval($idx->Non_unique == 0); + } else { + if (!empty($index[$idx->Key_name]['column']) && !is_array($index[$idx->Key_name]['column'])) { + $col[] = $index[$idx->Key_name]['column']; + } + $col[] = $idx->Column_name; + $index[$idx->Key_name]['column'] = $col; + } + } + $indices->closeCursor(); + } + return $index; + } + +/** + * Generate a MySQL Alter Table syntax for the given Schema comparison + * + * @param array $compare Result of a CakeSchema::compare() + * @param string $table + * @return array Array of alter statements to make. + */ + public function alterSchema($compare, $table = null) { + if (!is_array($compare)) { + return false; + } + $out = ''; + $colList = array(); + foreach ($compare as $curTable => $types) { + $indexes = $tableParameters = $colList = array(); + if (!$table || $table == $curTable) { + $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n"; + foreach ($types as $type => $column) { + if (isset($column['indexes'])) { + $indexes[$type] = $column['indexes']; + unset($column['indexes']); + } + if (isset($column['tableParameters'])) { + $tableParameters[$type] = $column['tableParameters']; + unset($column['tableParameters']); + } + switch ($type) { + case 'add': + foreach ($column as $field => $col) { + $col['name'] = $field; + $alter = 'ADD ' . $this->buildColumn($col); + if (isset($col['after'])) { + $alter .= ' AFTER ' . $this->name($col['after']); + } + $colList[] = $alter; + } + break; + case 'drop': + foreach ($column as $field => $col) { + $col['name'] = $field; + $colList[] = 'DROP ' . $this->name($field); + } + break; + case 'change': + foreach ($column as $field => $col) { + if (!isset($col['name'])) { + $col['name'] = $field; + } + $colList[] = 'CHANGE ' . $this->name($field) . ' ' . $this->buildColumn($col); + } + break; + } + } + $colList = array_merge($colList, $this->_alterIndexes($curTable, $indexes)); + $colList = array_merge($colList, $this->_alterTableParameters($curTable, $tableParameters)); + $out .= "\t" . implode(",\n\t", $colList) . ";\n\n"; + } + } + return $out; + } + +/** + * Generate a MySQL "drop table" statement for the given Schema object + * + * @param CakeSchema $schema An instance of a subclass of CakeSchema + * @param string $table Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + public function dropSchema(CakeSchema $schema, $table = null) { + $out = ''; + foreach ($schema->tables as $curTable => $columns) { + if (!$table || $table === $curTable) { + $out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n"; + } + } + return $out; + } + +/** + * Generate MySQL table parameter alteration statementes for a table. + * + * @param string $table Table to alter parameters for. + * @param array $parameters Parameters to add & drop. + * @return array Array of table property alteration statementes. + * @todo Implement this method. + */ + protected function _alterTableParameters($table, $parameters) { + if (isset($parameters['change'])) { + return $this->buildTableParameters($parameters['change']); + } + return array(); + } + +/** + * Generate MySQL index alteration statements for a table. + * + * @param string $table Table to alter indexes for + * @param array $indexes Indexes to add and drop + * @return array Index alteration statements + */ + protected function _alterIndexes($table, $indexes) { + $alter = array(); + if (isset($indexes['drop'])) { + foreach($indexes['drop'] as $name => $value) { + $out = 'DROP '; + if ($name == 'PRIMARY') { + $out .= 'PRIMARY KEY'; + } else { + $out .= 'KEY ' . $name; + } + $alter[] = $out; + } + } + if (isset($indexes['add'])) { + foreach ($indexes['add'] as $name => $value) { + $out = 'ADD '; + if ($name == 'PRIMARY') { + $out .= 'PRIMARY '; + $name = null; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + } + if (is_array($value['column'])) { + $out .= 'KEY '. $name .' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; + } else { + $out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')'; + } + $alter[] = $out; + } + } + return $alter; + } + +/** + * Returns an detailed array of sources (tables) in the database. + * + * @param string $name Table name to get parameters + * @return array Array of tablenames in the database + */ + public function listDetailedSources($name = null) { + $condition = ''; + $params = array(); + if (is_string($name)) { + $condition = ' WHERE name = ?' ; + $params = array($name); + } + $result = $this->_execute('SHOW TABLE STATUS ' . $condition, $params); + + if (!$result) { + $result->closeCursor(); + return array(); + } else { + $tables = array(); + while ($row = $result->fetch()) { + $tables[$row->Name] = (array) $row; + unset($tables[$row->Name]['queryString']); + if (!empty($row->Collation)) { + $charset = $this->getCharsetName($row->Collation); + if ($charset) { + $tables[$row->Name]['charset'] = $charset; + } + } + } + $result->closeCursor(); + if (is_string($name) && isset($tables[$name])) { + return $tables[$name]; + } + return $tables; + } + } + +/** + * Converts database-layer column types to basic types + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + * @return string Abstract column type (i.e. "string") + */ + public function column($real) { + if (is_array($real)) { + $col = $real['name']; + if (isset($real['limit'])) { + $col .= '(' . $real['limit'] . ')'; + } + return $col; + } + + $col = str_replace(')', '', $real); + $limit = $this->length($real); + if (strpos($col, '(') !== false) { + list($col, $vals) = explode('(', $col); + } + + if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) { + return $col; + } + if (($col === 'tinyint' && $limit == 1) || $col === 'boolean') { + return 'boolean'; + } + if (strpos($col, 'int') !== false) { + return 'integer'; + } + if (strpos($col, 'char') !== false || $col === 'tinytext') { + return 'string'; + } + if (strpos($col, 'text') !== false) { + return 'text'; + } + if (strpos($col, 'blob') !== false || $col === 'binary') { + return 'binary'; + } + if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) { + return 'float'; + } + if (strpos($col, 'enum') !== false) { + return "enum($vals)"; + } + return 'text'; + } +} diff --git a/app/Cake/Model/Datasource/Database/Postgres.php b/app/Cake/Model/Datasource/Database/Postgres.php new file mode 100644 index 00000000..aaf522d6 --- /dev/null +++ b/app/Cake/Model/Datasource/Database/Postgres.php @@ -0,0 +1,891 @@ + 'BEGIN', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); + +/** + * Base driver configuration settings. Merged with user settings. + * + * @var array + */ + protected $_baseConfig = array( + 'persistent' => true, + 'host' => 'localhost', + 'login' => 'root', + 'password' => '', + 'database' => 'cake', + 'schema' => 'public', + 'port' => 5432, + 'encoding' => '' + ); + +/** + * Columns + * + * @var array + */ + public $columns = array( + 'primary_key' => array('name' => 'serial NOT NULL'), + 'string' => array('name' => 'varchar', 'limit' => '255'), + 'text' => array('name' => 'text'), + 'integer' => array('name' => 'integer', 'formatter' => 'intval'), + 'float' => array('name' => 'float', 'formatter' => 'floatval'), + 'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'), + 'binary' => array('name' => 'bytea'), + 'boolean' => array('name' => 'boolean'), + 'number' => array('name' => 'numeric'), + 'inet' => array('name' => 'inet') + ); + +/** + * Starting Quote + * + * @var string + */ + public $startQuote = '"'; + +/** + * Ending Quote + * + * @var string + */ + public $endQuote = '"'; + +/** + * Contains mappings of custom auto-increment sequences, if a table uses a sequence name + * other than what is dictated by convention. + * + * @var array + */ + protected $_sequenceMap = array(); + +/** + * Connects to the database using options in the given configuration array. + * + * @return boolean True if successfully connected. + * @throws MissingConnectionException + */ + public function connect() { + $config = $this->config; + $this->connected = false; + try { + $flags = array( + PDO::ATTR_PERSISTENT => $config['persistent'], + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + $this->_connection = new PDO( + "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']}", + $config['login'], + $config['password'], + $flags + ); + + $this->connected = true; + if (!empty($config['encoding'])) { + $this->setEncoding($config['encoding']); + } + if (!empty($config['schema'])) { + $this->_execute('SET search_path TO ' . $config['schema']); + } + } catch (PDOException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); + } + + return $this->connected; + } + +/** + * Check if PostgreSQL is enabled/loaded + * + * @return boolean + */ + public function enabled() { + return in_array('pgsql', PDO::getAvailableDrivers()); + } + +/** + * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits. + * + * @param mixed $data + * @return array Array of tablenames in the database + */ + public function listSources($data = null) { + $cache = parent::listSources(); + + if ($cache != null) { + return $cache; + } + + $schema = $this->config['schema']; + $sql = "SELECT table_name as name FROM INFORMATION_SCHEMA.tables WHERE table_schema = ?"; + $result = $this->_execute($sql, array($schema)); + + if (!$result) { + return array(); + } else { + $tables = array(); + + foreach ($result as $item) { + $tables[] = $item->name; + } + + $result->closeCursor(); + parent::listSources($tables); + return $tables; + } + } + +/** + * Returns an array of the fields in given table name. + * + * @param Model $model Name of database table to inspect + * @return array Fields in table. Keys are name and type + */ + public function describe($model) { + $fields = parent::describe($model); + $table = $this->fullTableName($model, false); + $this->_sequenceMap[$table] = array(); + $cols = null; + + if ($fields === null) { + $cols = $this->_execute( + "SELECT DISTINCT column_name AS name, data_type AS type, is_nullable AS null, + column_default AS default, ordinal_position AS position, character_maximum_length AS char_length, + character_octet_length AS oct_length FROM information_schema.columns + WHERE table_name = ? AND table_schema = ? ORDER BY position", + array($table, $this->config['schema']) + ); + + foreach ($cols as $c) { + $type = $c->type; + if (!empty($c->oct_length) && $c->char_length === null) { + if ($c->type == 'character varying') { + $length = null; + $type = 'text'; + } else { + $length = intval($c->oct_length); + } + } elseif (!empty($c->char_length)) { + $length = intval($c->char_length); + } else { + $length = $this->length($c->type); + } + if (empty($length)) { + $length = null; + } + $fields[$c->name] = array( + 'type' => $this->column($type), + 'null' => ($c->null == 'NO' ? false : true), + 'default' => preg_replace( + "/^'(.*)'$/", + "$1", + preg_replace('/::.*/', '', $c->default) + ), + 'length' => $length + ); + if ($model instanceof Model) { + if ($c->name == $model->primaryKey) { + $fields[$c->name]['key'] = 'primary'; + if ($fields[$c->name]['type'] !== 'string') { + $fields[$c->name]['length'] = 11; + } + } + } + if ( + $fields[$c->name]['default'] == 'NULL' || + preg_match('/nextval\([\'"]?([\w.]+)/', $c->default, $seq) + ) { + $fields[$c->name]['default'] = null; + if (!empty($seq) && isset($seq[1])) { + $this->_sequenceMap[$table][$c->default] = $seq[1]; + } + } + if ($fields[$c->name]['type'] == 'boolean' && !empty($fields[$c->name]['default'])) { + $fields[$c->name]['default'] = constant($fields[$c->name]['default']); + } + } + $this->_cacheDescription($table, $fields); + } + if (isset($model->sequence)) { + $this->_sequenceMap[$table][$model->primaryKey] = $model->sequence; + } + + if ($cols) { + $cols->closeCursor(); + } + return $fields; + } + +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param string $source Name of the database table + * @param string $field Name of the ID database field. Defaults to "id" + * @return integer + */ + public function lastInsertId($source = null, $field = 'id') { + $seq = $this->getSequence($source, $field); + return $this->_connection->lastInsertId($seq); + } + +/** + * Gets the associated sequence for the given table/field + * + * @param mixed $table Either a full table name (with prefix) as a string, or a model object + * @param string $field Name of the ID database field. Defaults to "id" + * @return string The associated sequence name from the sequence map, defaults to "{$table}_{$field}_seq" + */ + public function getSequence($table, $field = 'id') { + if (is_object($table)) { + $table = $this->fullTableName($table, false); + } + if (isset($this->_sequenceMap[$table]) && isset($this->_sequenceMap[$table][$field])) { + return $this->_sequenceMap[$table][$field]; + } else { + return "{$table}_{$field}_seq"; + } + } + +/** + * Deletes all the records in a table and drops all associated auto-increment sequences + * + * @param mixed $table A string or model class representing the table to be truncated + * @param boolean $reset true for resseting the sequence, false to leave it as is. + * and if 1, sequences are not modified + * @return boolean SQL TRUNCATE TABLE statement, false if not applicable. + */ + public function truncate($table, $reset = 0) { + $table = $this->fullTableName($table, false); + if (!isset($this->_sequenceMap[$table])) { + $cache = $this->cacheSources; + $this->cacheSources = false; + $this->describe($table); + $this->cacheSources = $cache; + } + if ($this->execute('DELETE FROM ' . $this->fullTableName($table))) { + $table = $this->fullTableName($table, false); + if (isset($this->_sequenceMap[$table]) && $reset !== 1) { + foreach ($this->_sequenceMap[$table] as $field => $sequence) { + $this->_execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1"); + } + } + return true; + } + return false; + } + +/** + * Prepares field names to be quoted by parent + * + * @param string $data + * @return string SQL field + */ + public function name($data) { + if (is_string($data)) { + $data = str_replace('"__"', '__', $data); + } + return parent::name($data); + } + +/** + * Generates the fields list of an SQL query. + * + * @param Model $model + * @param string $alias Alias tablename + * @param mixed $fields + * @param boolean $quote + * @return array + */ + public function fields($model, $alias = null, $fields = array(), $quote = true) { + if (empty($alias)) { + $alias = $model->alias; + } + $fields = parent::fields($model, $alias, $fields, false); + + if (!$quote) { + return $fields; + } + $count = count($fields); + + if ($count >= 1 && !preg_match('/^\s*COUNT\(\*/', $fields[0])) { + $result = array(); + for ($i = 0; $i < $count; $i++) { + if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) { + if (substr($fields[$i], -1) == '*') { + if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') { + $build = explode('.', $fields[$i]); + $AssociatedModel = $model->{$build[0]}; + } else { + $AssociatedModel = $model; + } + + $_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema())); + $result = array_merge($result, $_fields); + continue; + } + + $prepend = ''; + if (strpos($fields[$i], 'DISTINCT') !== false) { + $prepend = 'DISTINCT '; + $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); + } + + if (strrpos($fields[$i], '.') === false) { + $fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]); + } else { + $build = explode('.', $fields[$i]); + $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]); + } + } else { + $fields[$i] = preg_replace_callback('/\(([\s\.\w]+)\)/', array(&$this, '_quoteFunctionField'), $fields[$i]); + } + $result[] = $fields[$i]; + } + return $result; + } + return $fields; + } + +/** + * Auxiliary function to quote matched `(Model.fields)` from a preg_replace_callback call + * + * @param string $match matched string + * @return string quoted strig + */ + protected function _quoteFunctionField($match) { + $prepend = ''; + if (strpos($match[1], 'DISTINCT') !== false) { + $prepend = 'DISTINCT '; + $match[1] = trim(str_replace('DISTINCT', '', $match[1])); + } + if (strpos($match[1], '.') === false) { + $match[1] = $this->name($match[1]); + } else { + $parts = explode('.', $match[1]); + if (!Set::numeric($parts)) { + $match[1] = $this->name($match[1]); + } + } + return '(' . $prepend .$match[1] . ')'; + } + +/** + * Returns an array of the indexes in given datasource name. + * + * @param string $model Name of model to inspect + * @return array Fields in table. Keys are column and unique + */ + public function index($model) { + $index = array(); + $table = $this->fullTableName($model, false); + if ($table) { + $indexes = $this->query("SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, i.indisvalid, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) as statement, c2.reltablespace + FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i + WHERE c.oid = ( + SELECT c.oid + FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + WHERE c.relname ~ '^(" . $table . ")$' + AND pg_catalog.pg_table_is_visible(c.oid) + AND n.nspname ~ '^(" . $this->config['schema'] . ")$' + ) + AND c.oid = i.indrelid AND i.indexrelid = c2.oid + ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false); + foreach ($indexes as $i => $info) { + $key = array_pop($info); + if ($key['indisprimary']) { + $key['relname'] = 'PRIMARY'; + } + $col = array(); + preg_match('/\(([^\)]+)\)/', $key['statement'], $indexColumns); + $parsedColumn = $indexColumns[1]; + if (strpos($indexColumns[1], ',') !== false) { + $parsedColumn = explode(', ', $indexColumns[1]); + } + $index[$key['relname']]['unique'] = $key['indisunique']; + $index[$key['relname']]['column'] = $parsedColumn; + } + } + return $index; + } + +/** + * Alter the Schema of a table. + * + * @param array $compare Results of CakeSchema::compare() + * @param string $table name of the table + * @return array + */ + public function alterSchema($compare, $table = null) { + if (!is_array($compare)) { + return false; + } + $out = ''; + $colList = array(); + foreach ($compare as $curTable => $types) { + $indexes = $colList = array(); + if (!$table || $table == $curTable) { + $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n"; + foreach ($types as $type => $column) { + if (isset($column['indexes'])) { + $indexes[$type] = $column['indexes']; + unset($column['indexes']); + } + switch ($type) { + case 'add': + foreach ($column as $field => $col) { + $col['name'] = $field; + $alter = 'ADD COLUMN '.$this->buildColumn($col); + if (isset($col['after'])) { + $alter .= ' AFTER '. $this->name($col['after']); + } + $colList[] = $alter; + } + break; + case 'drop': + foreach ($column as $field => $col) { + $col['name'] = $field; + $colList[] = 'DROP COLUMN '.$this->name($field); + } + break; + case 'change': + foreach ($column as $field => $col) { + if (!isset($col['name'])) { + $col['name'] = $field; + } + $fieldName = $this->name($field); + + $default = isset($col['default']) ? $col['default'] : null; + $nullable = isset($col['null']) ? $col['null'] : null; + unset($col['default'], $col['null']); + $colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace($fieldName, '', $this->buildColumn($col)); + + if (isset($nullable)) { + $nullable = ($nullable) ? 'DROP NOT NULL' : 'SET NOT NULL'; + $colList[] = 'ALTER COLUMN '. $fieldName .' ' . $nullable; + } + + if (isset($default)) { + $colList[] = 'ALTER COLUMN '. $fieldName .' SET DEFAULT ' . $this->value($default, $col['type']); + } else { + $colList[] = 'ALTER COLUMN '. $fieldName .' DROP DEFAULT'; + } + + } + break; + } + } + if (isset($indexes['drop']['PRIMARY'])) { + $colList[] = 'DROP CONSTRAINT ' . $curTable . '_pkey'; + } + if (isset($indexes['add']['PRIMARY'])) { + $cols = $indexes['add']['PRIMARY']['column']; + if (is_array($cols)) { + $cols = implode(', ', $cols); + } + $colList[] = 'ADD PRIMARY KEY (' . $cols . ')'; + } + + if (!empty($colList)) { + $out .= "\t" . implode(",\n\t", $colList) . ";\n\n"; + } else { + $out = ''; + } + $out .= implode(";\n\t", $this->_alterIndexes($curTable, $indexes)); + } + } + return $out; + } + +/** + * Generate PostgreSQL index alteration statements for a table. + * + * @param string $table Table to alter indexes for + * @param array $indexes Indexes to add and drop + * @return array Index alteration statements + */ + protected function _alterIndexes($table, $indexes) { + $alter = array(); + if (isset($indexes['drop'])) { + foreach($indexes['drop'] as $name => $value) { + $out = 'DROP '; + if ($name == 'PRIMARY') { + continue; + } else { + $out .= 'INDEX ' . $name; + } + $alter[] = $out; + } + } + if (isset($indexes['add'])) { + foreach ($indexes['add'] as $name => $value) { + $out = 'CREATE '; + if ($name == 'PRIMARY') { + continue; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + $out .= 'INDEX '; + } + if (is_array($value['column'])) { + $out .= $name . ' ON ' . $table . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; + } else { + $out .= $name . ' ON ' . $table . ' (' . $this->name($value['column']) . ')'; + } + $alter[] = $out; + } + } + return $alter; + } + +/** + * Returns a limit statement in the correct format for the particular database. + * + * @param integer $limit Limit of results returned + * @param integer $offset Offset from which to start results + * @return string SQL limit/offset statement + */ + public function limit($limit, $offset = null) { + if ($limit) { + $rt = ''; + if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) { + $rt = ' LIMIT'; + } + + $rt .= ' ' . $limit; + if ($offset) { + $rt .= ' OFFSET ' . $offset; + } + + return $rt; + } + return null; + } + +/** + * Converts database-layer column types to basic types + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + * @return string Abstract column type (i.e. "string") + */ + public function column($real) { + if (is_array($real)) { + $col = $real['name']; + if (isset($real['limit'])) { + $col .= '(' . $real['limit'] . ')'; + } + return $col; + } + + $col = str_replace(')', '', $real); + $limit = null; + + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } + + $floats = array( + 'float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'real', 'numeric' + ); + + switch (true) { + case (in_array($col, array('date', 'time', 'inet', 'boolean'))): + return $col; + case (strpos($col, 'timestamp') !== false): + return 'datetime'; + case (strpos($col, 'time') === 0): + return 'time'; + case (strpos($col, 'int') !== false && $col != 'interval'): + return 'integer'; + case (strpos($col, 'char') !== false || $col == 'uuid'): + return 'string'; + case (strpos($col, 'text') !== false): + return 'text'; + case (strpos($col, 'bytea') !== false): + return 'binary'; + case (in_array($col, $floats)): + return 'float'; + default: + return 'text'; + break; + } + } + +/** + * Gets the length of a database-native column description, or null if no length + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + * @return integer An integer representing the length of the column + */ + public function length($real) { + $col = str_replace(array(')', 'unsigned'), '', $real); + $limit = null; + + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } + if ($col == 'uuid') { + return 36; + } + if ($limit != null) { + return intval($limit); + } + return null; + } + +/** + * resultSet method + * + * @param array $results + * @return void + */ + public function resultSet(&$results) { + $this->map = array(); + $numFields = $results->columnCount(); + $index = 0; + $j = 0; + + while ($j < $numFields) { + $column = $results->getColumnMeta($j); + if (strpos($column['name'], '__')) { + list($table, $name) = explode('__', $column['name']); + $this->map[$index++] = array($table, $name, $column['native_type']); + } else { + $this->map[$index++] = array(0, $column['name'], $column['native_type']); + } + $j++; + } + } + +/** + * Fetches the next row from the current result set + * + * @return array + */ + public function fetchResult() { + if ($row = $this->_result->fetch()) { + $resultRow = array(); + + foreach ($this->map as $index => $meta) { + list($table, $column, $type) = $meta; + + switch ($type) { + case 'bool': + $resultRow[$table][$column] = is_null($row[$index]) ? null : $this->boolean($row[$index]); + break; + case 'binary': + case 'bytea': + $resultRow[$table][$column] = stream_get_contents($row[$index]); + break; + default: + $resultRow[$table][$column] = $row[$index]; + break; + } + } + return $resultRow; + } else { + $this->_result->closeCursor(); + return false; + } + } + +/** + * Translates between PHP boolean values and PostgreSQL boolean values + * + * @param mixed $data Value to be translated + * @param boolean $quote true to quote a boolean to be used in a query, flase to return the boolean value + * @return boolean Converted boolean value + */ + public function boolean($data, $quote = false) { + switch (true) { + case ($data === true || $data === false): + $result = $data; + break; + case ($data === 't' || $data === 'f'): + $result = ($data === 't'); + break; + case ($data === 'true' || $data === 'false'): + $result = ($data === 'true'); + break; + case ($data === 'TRUE' || $data === 'FALSE'): + $result = ($data === 'TRUE'); + break; + default: + $result = (bool) $data; + break; + } + + if ($quote) { + return ($result) ? 'TRUE' : 'FALSE'; + } + return (bool) $result; + } + +/** + * Sets the database encoding + * + * @param mixed $enc Database encoding + * @return boolean True on success, false on failure + */ + public function setEncoding($enc) { + if ($this->_execute('SET NAMES ?', array($enc))) { + return true; + } + return false; + } + +/** + * Gets the database encoding + * + * @return string The database encoding + */ + public function getEncoding() { + $cosa = $this->_execute('SHOW client_encoding')->fetch(); + } + +/** + * Generate a Postgres-native column schema string + * + * @param array $column An array structured like the following: + * array('name'=>'value', 'type'=>'value'[, options]), + * where options can be 'default', 'length', or 'key'. + * @return string + */ + public function buildColumn($column) { + $col = $this->columns[$column['type']]; + if (!isset($col['length']) && !isset($col['limit'])) { + unset($column['length']); + } + $out = preg_replace('/integer\([0-9]+\)/', 'integer', parent::buildColumn($column)); + $out = str_replace('integer serial', 'serial', $out); + if (strpos($out, 'timestamp DEFAULT')) { + if (isset($column['null']) && $column['null']) { + $out = str_replace('DEFAULT NULL', '', $out); + } else { + $out = str_replace('DEFAULT NOT NULL', '', $out); + } + } + if (strpos($out, 'DEFAULT DEFAULT')) { + if (isset($column['null']) && $column['null']) { + $out = str_replace('DEFAULT DEFAULT', 'DEFAULT NULL', $out); + } elseif (in_array($column['type'], array('integer', 'float'))) { + $out = str_replace('DEFAULT DEFAULT', 'DEFAULT 0', $out); + } elseif ($column['type'] == 'boolean') { + $out = str_replace('DEFAULT DEFAULT', 'DEFAULT FALSE', $out); + } + } + return $out; + } + +/** + * Format indexes for create table + * + * @param array $indexes + * @param string $table + * @return string + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + if (!is_array($indexes)) { + return array(); + } + foreach ($indexes as $name => $value) { + if ($name == 'PRIMARY') { + $out = 'PRIMARY KEY (' . $this->name($value['column']) . ')'; + } else { + $out = 'CREATE '; + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + if (is_array($value['column'])) { + $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column'])); + } else { + $value['column'] = $this->name($value['column']); + } + $out .= "INDEX {$name} ON {$table}({$value['column']});"; + } + $join[] = $out; + } + return $join; + } + +/** + * Overrides DboSource::renderStatement to handle schema generation with Postgres-style indexes + * + * @param string $type + * @param array $data + * @return string + */ + public function renderStatement($type, $data) { + switch (strtolower($type)) { + case 'schema': + extract($data); + + foreach ($indexes as $i => $index) { + if (preg_match('/PRIMARY KEY/', $index)) { + unset($indexes[$i]); + $columns[] = $index; + break; + } + } + $join = array('columns' => ",\n\t", 'indexes' => "\n"); + + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = implode($join[$var], array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } +} diff --git a/app/Cake/Model/Datasource/Database/Sqlite.php b/app/Cake/Model/Datasource/Database/Sqlite.php new file mode 100644 index 00000000..ed34d3a6 --- /dev/null +++ b/app/Cake/Model/Datasource/Database/Sqlite.php @@ -0,0 +1,545 @@ + false, + 'database' => null + ); + +/** + * SQLite3 column definition + * + * @var array + */ + public $columns = array( + 'primary_key' => array('name' => 'integer primary key autoincrement'), + 'string' => array('name' => 'varchar', 'limit' => '255'), + 'text' => array('name' => 'text'), + 'integer' => array('name' => 'integer', 'limit' => null, 'formatter' => 'intval'), + 'float' => array('name' => 'float', 'formatter' => 'floatval'), + 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'), + 'binary' => array('name' => 'blob'), + 'boolean' => array('name' => 'boolean') + ); + +/** + * List of engine specific additional field parameters used on table creating + * + * @var array + */ + public $fieldParameters = array( + 'collate' => array( + 'value' => 'COLLATE', + 'quote' => false, + 'join' => ' ', + 'column' => 'Collate', + 'position' => 'afterDefault', + 'options' => array( + 'BINARY', 'NOCASE', 'RTRIM' + ) + ), + ); + +/** + * Connects to the database using config['database'] as a filename. + * + * @return boolean + * @throws MissingConnectionException + */ + public function connect() { + $config = $this->config; + $flags = array( + PDO::ATTR_PERSISTENT => $config['persistent'], + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + try { + $this->_connection = new PDO('sqlite:' . $config['database'], null, null, $flags); + $this->connected = true; + } catch(PDOException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); + } + return $this->connected; + } + +/** + * Check whether the MySQL extension is installed/loaded + * + * @return boolean + */ + public function enabled() { + return in_array('sqlite', PDO::getAvailableDrivers()); + } + +/** + * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits. + * + * @param mixed $data + * @return array Array of tablenames in the database + */ + public function listSources($data = null) { + $cache = parent::listSources(); + if ($cache != null) { + return $cache; + } + + $result = $this->fetchAll("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", false); + + if (!$result || empty($result)) { + return array(); + } else { + $tables = array(); + foreach ($result as $table) { + $tables[] = $table[0]['name']; + } + parent::listSources($tables); + return $tables; + } + return array(); + } + +/** + * Returns an array of the fields in given table name. + * + * @param Model $model + * @return array Fields in table. Keys are name and type + */ + public function describe(Model $model) { + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + $fields = array(); + $result = $this->_execute('PRAGMA table_info(' . $model->tablePrefix . $model->table . ')'); + + foreach ($result as $column) { + $column = (array) $column; + $default = ($column['dflt_value'] === 'NULL') ? null : trim($column['dflt_value'], "'"); + + $fields[$column['name']] = array( + 'type' => $this->column($column['type']), + 'null' => !$column['notnull'], + 'default' => $default, + 'length' => $this->length($column['type']) + ); + if ($column['pk'] == 1) { + $fields[$column['name']]['key'] = $this->index['PRI']; + $fields[$column['name']]['null'] = false; + if (empty($fields[$column['name']]['length'])) { + $fields[$column['name']]['length'] = 11; + } + } + } + + $result->closeCursor(); + $this->_cacheDescription($model->tablePrefix . $model->table, $fields); + return $fields; + } + +/** + * Generates and executes an SQL UPDATE statement for given model, fields, and values. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return array + */ + public function update(Model $model, $fields = array(), $values = null, $conditions = null) { + if (empty($values) && !empty($fields)) { + foreach ($fields as $field => $value) { + if (strpos($field, $model->alias . '.') !== false) { + unset($fields[$field]); + $field = str_replace($model->alias . '.', "", $field); + $field = str_replace($model->alias . '.', "", $field); + $fields[$field] = $value; + } + } + } + return parent::update($model, $fields, $values, $conditions); + } + +/** + * Deletes all the records in a table and resets the count of the auto-incrementing + * primary key, where applicable. + * + * @param mixed $table A string or model class representing the table to be truncated + * @return boolean SQL TRUNCATE TABLE statement, false if not applicable. + */ + public function truncate($table) { + $this->_execute('DELETE FROM sqlite_sequence where name=' . $this->fullTableName($table)); + return $this->execute('DELETE FROM ' . $this->fullTableName($table)); + } + +/** + * Converts database-layer column types to basic types + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + * @return string Abstract column type (i.e. "string") + */ + public function column($real) { + if (is_array($real)) { + $col = $real['name']; + if (isset($real['limit'])) { + $col .= '('.$real['limit'].')'; + } + return $col; + } + + $col = strtolower(str_replace(')', '', $real)); + $limit = null; + @list($col, $limit) = explode('(', $col); + + if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) { + return $col; + } + if (strpos($col, 'varchar') !== false) { + return 'string'; + } + if (in_array($col, array('blob', 'clob'))) { + return 'binary'; + } + if (strpos($col, 'numeric') !== false || strpos($col, 'decimal') !== false) { + return 'float'; + } + return 'text'; + } + +/** + * Generate ResultSet + * + * @param mixed $results + * @return void + */ + public function resultSet($results) { + $this->results = $results; + $this->map = array(); + $num_fields = $results->columnCount(); + $index = 0; + $j = 0; + + //PDO::getColumnMeta is experimental and does not work with sqlite3, + // so try to figure it out based on the querystring + $querystring = $results->queryString; + if (stripos($querystring, 'SELECT') === 0) { + $last = strripos($querystring, 'FROM'); + if ($last !== false) { + $selectpart = substr($querystring, 7, $last - 8); + $selects = explode(',', $selectpart); + } + } elseif (strpos($querystring, 'PRAGMA table_info') === 0) { + $selects = array('cid', 'name', 'type', 'notnull', 'dflt_value', 'pk'); + } elseif(strpos($querystring, 'PRAGMA index_list') === 0) { + $selects = array('seq', 'name', 'unique'); + } elseif(strpos($querystring, 'PRAGMA index_info') === 0) { + $selects = array('seqno', 'cid', 'name'); + } + while ($j < $num_fields) { + if (!isset($selects[$j])) { + $j++; + continue; + } + if (preg_match('/\bAS\s+(.*)/i', $selects[$j], $matches)) { + $columnName = trim($matches[1],'"'); + } else { + $columnName = trim(str_replace('"', '', $selects[$j])); + } + + if (strpos($selects[$j], 'DISTINCT') === 0) { + $columnName = str_ireplace('DISTINCT', '', $columnName); + } + + $metaType = false; + try { + $metaData = (array)$results->getColumnMeta($j); + if (!empty($metaData['sqlite:decl_type'])) { + $metaType = trim($metaData['sqlite:decl_type']); + } + } catch (Exception $e) {} + + if (strpos($columnName, '.')) { + $parts = explode('.', $columnName); + $this->map[$index++] = array(trim($parts[0]), trim($parts[1]), $metaType); + } else { + $this->map[$index++] = array(0, $columnName, $metaType); + } + $j++; + } + } + +/** + * Fetches the next row from the current result set + * + * @return mixed array with results fetched and mapped to column names or false if there is no results left to fetch + */ + public function fetchResult() { + if ($row = $this->_result->fetch()) { + $resultRow = array(); + foreach ($this->map as $col => $meta) { + list($table, $column, $type) = $meta; + $resultRow[$table][$column] = $row[$col]; + if ($type == 'boolean' && !is_null($row[$col])) { + $resultRow[$table][$column] = $this->boolean($resultRow[$table][$column]); + } + } + return $resultRow; + } else { + $this->_result->closeCursor(); + return false; + } + } + + +/** + * Returns a limit statement in the correct format for the particular database. + * + * @param integer $limit Limit of results returned + * @param integer $offset Offset from which to start results + * @return string SQL limit/offset statement + */ + public function limit($limit, $offset = null) { + if ($limit) { + $rt = ''; + if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) { + $rt = ' LIMIT'; + } + $rt .= ' ' . $limit; + if ($offset) { + $rt .= ' OFFSET ' . $offset; + } + return $rt; + } + return null; + } + +/** + * Generate a database-native column schema string + * + * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]), + * where options can be 'default', 'length', or 'key'. + * @return string + */ + public function buildColumn($column) { + $name = $type = null; + $column = array_merge(array('null' => true), $column); + extract($column); + + if (empty($name) || empty($type)) { + trigger_error(__d('cake_dev', 'Column name or type not defined in schema'), E_USER_WARNING); + return null; + } + + if (!isset($this->columns[$type])) { + trigger_error(__d('cake_dev', 'Column type %s does not exist', $type), E_USER_WARNING); + return null; + } + + $real = $this->columns[$type]; + $out = $this->name($name) . ' ' . $real['name']; + if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') { + return $this->name($name) . ' ' . $this->columns['primary_key']['name']; + } + return parent::buildColumn($column); + } + +/** + * Sets the database encoding + * + * @param string $enc Database encoding + * @return boolean + */ + public function setEncoding($enc) { + if (!in_array($enc, array("UTF-8", "UTF-16", "UTF-16le", "UTF-16be"))) { + return false; + } + return $this->_execute("PRAGMA encoding = \"{$enc}\"") !== false; + } + +/** + * Gets the database encoding + * + * @return string The database encoding + */ + public function getEncoding() { + return $this->fetchRow('PRAGMA encoding'); + } + +/** + * Removes redundant primary key indexes, as they are handled in the column def of the key. + * + * @param array $indexes + * @param string $table + * @return string + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + + foreach ($indexes as $name => $value) { + + if ($name == 'PRIMARY') { + continue; + } + $out = 'CREATE '; + + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + if (is_array($value['column'])) { + $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column'])); + } else { + $value['column'] = $this->name($value['column']); + } + $t = trim($table, '"'); + $out .= "INDEX {$t}_{$name} ON {$table}({$value['column']});"; + $join[] = $out; + } + return $join; + } + +/** + * Overrides DboSource::index to handle SQLite indexe introspection + * Returns an array of the indexes in given table name. + * + * @param string $model Name of model to inspect + * @return array Fields in table. Keys are column and unique + */ + public function index($model) { + $index = array(); + $table = $this->fullTableName($model); + if ($table) { + $indexes = $this->query('PRAGMA index_list(' . $table . ')'); + + if (is_bool($indexes)) { + return array(); + } + foreach ($indexes as $i => $info) { + $key = array_pop($info); + $keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")'); + foreach ($keyInfo as $keyCol) { + if (!isset($index[$key['name']])) { + $col = array(); + if (preg_match('/autoindex/', $key['name'])) { + $key['name'] = 'PRIMARY'; + } + $index[$key['name']]['column'] = $keyCol[0]['name']; + $index[$key['name']]['unique'] = intval($key['unique'] == 1); + } else { + if (!is_array($index[$key['name']]['column'])) { + $col[] = $index[$key['name']]['column']; + } + $col[] = $keyCol[0]['name']; + $index[$key['name']]['column'] = $col; + } + } + } + } + return $index; + } + +/** + * Overrides DboSource::renderStatement to handle schema generation with SQLite-style indexes + * + * @param string $type + * @param array $data + * @return string + */ + public function renderStatement($type, $data) { + switch (strtolower($type)) { + case 'schema': + extract($data); + if (is_array($columns)) { + $columns = "\t" . join(",\n\t", array_filter($columns)); + } + if (is_array($indexes)) { + $indexes = "\t" . join("\n\t", array_filter($indexes)); + } + return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } + +/** + * PDO deals in objects, not resources, so overload accordingly. + * + * @return boolean + */ + public function hasResult() { + return is_object($this->_result); + } + +/** + * Generate a "drop table" statement for the given Schema object + * + * @param CakeSchema $schema An instance of a subclass of CakeSchema + * @param string $table Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + public function dropSchema(CakeSchema $schema, $table = null) { + $out = ''; + foreach ($schema->tables as $curTable => $columns) { + if (!$table || $table == $curTable) { + $out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n"; + } + } + return $out; + } +} diff --git a/app/Cake/Model/Datasource/Database/Sqlserver.php b/app/Cake/Model/Datasource/Database/Sqlserver.php new file mode 100644 index 00000000..e897a2ea --- /dev/null +++ b/app/Cake/Model/Datasource/Database/Sqlserver.php @@ -0,0 +1,795 @@ + true, + 'host' => 'localhost\SQLEXPRESS', + 'login' => '', + 'password' => '', + 'database' => 'cake' + ); + +/** + * MS SQL column definition + * + * @var array + */ + public $columns = array( + 'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'), + 'string' => array('name' => 'nvarchar', 'limit' => '255'), + 'text' => array('name' => 'nvarchar', 'limit' => 'MAX'), + 'integer' => array('name' => 'int', 'formatter' => 'intval'), + 'float' => array('name' => 'numeric', 'formatter' => 'floatval'), + 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'), + 'binary' => array('name' => 'varbinary'), + 'boolean' => array('name' => 'bit') + ); + +/** + * Index of basic SQL commands + * + * @var array + */ + protected $_commands = array( + 'begin' => 'BEGIN TRANSACTION', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); + +/** + * Magic column name used to provide pagination support for SQLServer 2008 + * which lacks proper limit/offset support. + */ + const ROW_COUNTER = '_cake_page_rownum_'; + +/** + * The version of SQLServer being used. If greater than 11 + * Normal limit offset statements will be used + * + * @var string + */ + protected $_version; + +/** + * Connects to the database using options in the given configuration array. + * + * @return boolean True if the database could be connected, else false + * @throws MissingConnectionException + */ + public function connect() { + $config = $this->config; + $this->connected = false; + try { + $flags = array( + PDO::ATTR_PERSISTENT => $config['persistent'], + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + if (!empty($config['encoding'])) { + $flags[PDO::SQLSRV_ATTR_ENCODING] = $config['encoding']; + } + $this->_connection = new PDO( + "sqlsrv:server={$config['host']};Database={$config['database']}", + $config['login'], + $config['password'], + $flags + ); + $this->connected = true; + } catch (PDOException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); + } + + $this->_version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION); + return $this->connected; + } + +/** + * Check that PDO SQL Server is installed/loaded + * + * @return boolean + */ + public function enabled() { + return in_array('sqlsrv', PDO::getAvailableDrivers()); + } + +/** + * Returns an array of sources (tables) in the database. + * + * @param mixed $data + * @return array Array of tablenames in the database + */ + public function listSources($data = null) { + $cache = parent::listSources(); + if ($cache !== null) { + return $cache; + } + $result = $this->_execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"); + + if (!$result) { + $result->closeCursor(); + return array(); + } else { + $tables = array(); + + while ($line = $result->fetch()) { + $tables[] = $line[0]; + } + + $result->closeCursor(); + parent::listSources($tables); + return $tables; + } + } + +/** + * Returns an array of the fields in given table name. + * + * @param Model $model Model object to describe + * @return array Fields in table. Keys are name and type + * @throws CakeException + */ + public function describe(Model $model) { + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + $fields = array(); + $table = $this->fullTableName($model, false); + $cols = $this->_execute( + "SELECT + COLUMN_NAME as Field, + DATA_TYPE as Type, + COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, + IS_NULLABLE As [Null], + COLUMN_DEFAULT as [Default], + COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], + NUMERIC_SCALE as Size + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = '" . $table . "'" + ); + if (!$cols) { + throw new CakeException(__d('cake_dev', 'Could not describe table for %s', $model->name)); + } + + foreach ($cols as $column) { + $field = $column->Field; + $fields[$field] = array( + 'type' => $this->column($column), + 'null' => ($column->Null === 'YES' ? true : false), + 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column->Default), + 'length' => $this->length($column), + 'key' => ($column->Key == '1') ? 'primary' : false + ); + + if ($fields[$field]['default'] === 'null') { + $fields[$field]['default'] = null; + } else { + $this->value($fields[$field]['default'], $fields[$field]['type']); + } + + if ($fields[$field]['key'] !== false && $fields[$field]['type'] == 'integer') { + $fields[$field]['length'] = 11; + } elseif ($fields[$field]['key'] === false) { + unset($fields[$field]['key']); + } + if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) { + $fields[$field]['length'] = null; + } + if ($fields[$field]['type'] == 'float' && !empty($column->Size)) { + $fields[$field]['length'] = $fields[$field]['length'] . ',' . $column->Size; + } + } + $this->_cacheDescription($table, $fields); + $cols->closeCursor(); + return $fields; + } + + +/** + * Generates the fields list of an SQL query. + * + * @param Model $model + * @param string $alias Alias tablename + * @param array $fields + * @param boolean $quote + * @return array + */ + public function fields($model, $alias = null, $fields = array(), $quote = true) { + if (empty($alias)) { + $alias = $model->alias; + } + $fields = parent::fields($model, $alias, $fields, false); + $count = count($fields); + + if ($count >= 1 && strpos($fields[0], 'COUNT(*)') === false) { + $result = array(); + for ($i = 0; $i < $count; $i++) { + $prepend = ''; + + if (strpos($fields[$i], 'DISTINCT') !== false) { + $prepend = 'DISTINCT '; + $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); + } + + if (!preg_match('/\s+AS\s+/i', $fields[$i])) { + if (substr($fields[$i], -1) == '*') { + if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') { + $build = explode('.', $fields[$i]); + $AssociatedModel = $model->{$build[0]}; + } else { + $AssociatedModel = $model; + } + + $_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema())); + $result = array_merge($result, $_fields); + continue; + } + + if (strpos($fields[$i], '.') === false) { + $this->_fieldMappings[$alias . '__' . $fields[$i]] = $alias . '.' . $fields[$i]; + $fieldName = $this->name($alias . '.' . $fields[$i]); + $fieldAlias = $this->name($alias . '__' . $fields[$i]); + } else { + $build = explode('.', $fields[$i]); + $this->_fieldMappings[$build[0] . '__' .$build[1]] = $fields[$i]; + $fieldName = $this->name($build[0] . '.' . $build[1]); + $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $build[1]); + } + if ($model->getColumnType($fields[$i]) == 'datetime') { + $fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)"; + } + $fields[$i] = "{$fieldName} AS {$fieldAlias}"; + } + $result[] = $prepend . $fields[$i]; + } + return $result; + } else { + return $fields; + } + } + +/** + * Generates and executes an SQL INSERT statement for given model, fields, and values. + * Removes Identity (primary key) column from update data before returning to parent, if + * value is empty. + * + * @param Model $model + * @param array $fields + * @param array $values + * @return array + */ + public function create(Model $model, $fields = null, $values = null) { + if (!empty($values)) { + $fields = array_combine($fields, $values); + } + $primaryKey = $this->_getPrimaryKey($model); + + if (array_key_exists($primaryKey, $fields)) { + if (empty($fields[$primaryKey])) { + unset($fields[$primaryKey]); + } else { + $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' ON'); + } + } + $result = parent::create($model, array_keys($fields), array_values($fields)); + if (array_key_exists($primaryKey, $fields) && !empty($fields[$primaryKey])) { + $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' OFF'); + } + return $result; + } + +/** + * Generates and executes an SQL UPDATE statement for given model, fields, and values. + * Removes Identity (primary key) column from update data before returning to parent. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return array + */ + public function update(Model $model, $fields = array(), $values = null, $conditions = null) { + if (!empty($values)) { + $fields = array_combine($fields, $values); + } + if (isset($fields[$model->primaryKey])) { + unset($fields[$model->primaryKey]); + } + if (empty($fields)) { + return true; + } + return parent::update($model, array_keys($fields), array_values($fields), $conditions); + } + +/** + * Returns a limit statement in the correct format for the particular database. + * + * @param integer $limit Limit of results returned + * @param integer $offset Offset from which to start results + * @return string SQL limit/offset statement + */ + public function limit($limit, $offset = null) { + if ($limit) { + $rt = ''; + if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) { + $rt = ' TOP'; + } + $rt .= ' ' . $limit; + if (is_int($offset) && $offset > 0) { + $rt = ' OFFSET ' . intval($offset) . ' ROWS FETCH FIRST ' . intval($limit) . ' ROWS ONLY'; + } + return $rt; + } + return null; + } + +/** + * Converts database-layer column types to basic types + * + * @param mixed $real Either the string value of the fields type. + * or the Result object from Sqlserver::describe() + * @return string Abstract column type (i.e. "string") + */ + public function column($real) { + $limit = null; + $col = $real; + if (is_object($real) && isset($real->Field)) { + $limit = $real->Length; + $col = $real->Type; + } + + if ($col == 'datetime2') { + return 'datetime'; + } + if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) { + return $col; + } + if ($col == 'bit') { + return 'boolean'; + } + if (strpos($col, 'int') !== false) { + return 'integer'; + } + if (strpos($col, 'char') !== false && $limit == -1) { + return 'text'; + } + if (strpos($col, 'char') !== false) { + return 'string'; + } + if (strpos($col, 'text') !== false) { + return 'text'; + } + if (strpos($col, 'binary') !== false || $col == 'image') { + return 'binary'; + } + if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) { + return 'float'; + } + return 'text'; + } + +/** + * Handle SQLServer specific length properties. + * SQLServer handles text types as nvarchar/varchar with a length of -1. + * + * @param mixed $length Either the length as a string, or a Column descriptor object. + * @return mixed null|integer with length of column. + */ + public function length($length) { + if (is_object($length) && isset($length->Length)) { + if ($length->Length == -1 && strpos($length->Type, 'char') !== false) { + return null; + } + if (in_array($length->Type, array('nchar', 'nvarchar'))) { + return floor($length->Length / 2); + } + return $length->Length; + } + return parent::length($length); + } + +/** + * Builds a map of the columns contained in a result + * + * @param PDOStatement $results + * @return void + */ + public function resultSet($results) { + $this->map = array(); + $numFields = $results->columnCount(); + $index = 0; + + while ($numFields-- > 0) { + $column = $results->getColumnMeta($index); + $name = $column['name']; + + if (strpos($name, '__')) { + if (isset($this->_fieldMappings[$name]) && strpos($this->_fieldMappings[$name], '.')) { + $map = explode('.', $this->_fieldMappings[$name]); + } elseif (isset($this->_fieldMappings[$name])) { + $map = array(0, $this->_fieldMappings[$name]); + } else { + $map = array(0, $name); + } + } else { + $map = array(0, $name); + } + $map[] = ($column['sqlsrv:decl_type'] == 'bit') ? 'boolean' : $column['native_type']; + $this->map[$index++] = $map; + } + } + +/** + * Builds final SQL statement + * + * @param string $type Query type + * @param array $data Query data + * @return string + */ + public function renderStatement($type, $data) { + switch (strtolower($type)) { + case 'select': + extract($data); + $fields = trim($fields); + + if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) { + $limit = 'DISTINCT ' . trim($limit); + $fields = substr($fields, 9); + } + + // hack order as SQLServer requires an order if there is a limit. + if ($limit && !$order) { + $order = 'ORDER BY (SELECT NULL)'; + } + + // For older versions use the subquery version of pagination. + if (version_compare($this->_version, '11', '<') && preg_match('/FETCH\sFIRST\s+([0-9]+)/i', $limit, $offset)) { + preg_match('/OFFSET\s*(\d+)\s*.*?(\d+)\s*ROWS/', $limit, $limitOffset); + + $limit = 'TOP ' . intval($limitOffset[2]); + $page = intval($limitOffset[1] / $limitOffset[2]); + $offset = intval($limitOffset[2] * $page); + + $rowCounter = self::ROW_COUNTER; + return " + SELECT {$limit} * FROM ( + SELECT {$fields}, ROW_NUMBER() OVER ({$order}) AS {$rowCounter} + FROM {$table} {$alias} {$joins} {$conditions} {$group} + ) AS _cake_paging_ + WHERE _cake_paging_.{$rowCounter} > {$offset} + ORDER BY _cake_paging_.{$rowCounter} + "; + } elseif (strpos($limit, 'FETCH') !== false) { + return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}"; + } else { + return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}"; + } + break; + case "schema": + extract($data); + + foreach ($indexes as $i => $index) { + if (preg_match('/PRIMARY KEY/', $index)) { + unset($indexes[$i]); + break; + } + } + + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . implode(",\n\t", array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } + +/** + * Returns a quoted and escaped string of $data for use in an SQL statement. + * + * @param string $data String to be prepared for use in an SQL statement + * @param string $column The column into which this data will be inserted + * @return string Quoted and escaped data + */ + public function value($data, $column = null) { + if (is_array($data) || is_object($data)) { + return parent::value($data, $column); + } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) { + return $data; + } + + if (empty($column)) { + $column = $this->introspectType($data); + } + + switch ($column) { + case 'string': + case 'text': + return 'N' . $this->_connection->quote($data, PDO::PARAM_STR); + default: + return parent::value($data, $column); + } + } +/** + * Returns an array of all result rows for a given SQL query. + * Returns false if no rows matched. + * + * @param Model $model + * @param array $queryData + * @param integer $recursive + * @return array Array of resultset rows, or false if no rows matched + */ + public function read(Model $model, $queryData = array(), $recursive = null) { + $results = parent::read($model, $queryData, $recursive); + $this->_fieldMappings = array(); + return $results; + } + +/** + * Fetches the next row from the current result set. + * Eats the magic ROW_COUNTER variable. + * + * @return mixed + */ + public function fetchResult() { + if ($row = $this->_result->fetch()) { + $resultRow = array(); + foreach ($this->map as $col => $meta) { + list($table, $column, $type) = $meta; + if ($table === 0 && $column === self::ROW_COUNTER) { + continue; + } + $resultRow[$table][$column] = $row[$col]; + if ($type === 'boolean' && !is_null($row[$col])) { + $resultRow[$table][$column] = $this->boolean($resultRow[$table][$column]); + } + } + return $resultRow; + } + $this->_result->closeCursor(); + return false; + } + +/** + * Inserts multiple values into a table + * + * @param string $table + * @param string $fields + * @param array $values + * @return void + */ + public function insertMulti($table, $fields, $values) { + $primaryKey = $this->_getPrimaryKey($table); + $hasPrimaryKey = $primaryKey != null && ( + (is_array($fields) && in_array($primaryKey, $fields) + || (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false)) + ); + + if ($hasPrimaryKey) { + $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON'); + } + + $table = $this->fullTableName($table); + $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); + $this->begin(); + foreach ($values as $value) { + $holder = implode(', ', array_map(array(&$this, 'value'), $value)); + $this->_execute("INSERT INTO {$table} ({$fields}) VALUES ({$holder})"); + } + $this->commit(); + + if ($hasPrimaryKey) { + $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF'); + } + } + +/** + * Generate a database-native column schema string + * + * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]), + * where options can be 'default', 'length', or 'key'. + * @return string + */ + public function buildColumn($column) { + $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column)); + if (strpos($result, 'DEFAULT NULL') !== false) { + if (isset($column['default']) && $column['default'] === '') { + $result = str_replace('DEFAULT NULL', "DEFAULT ''", $result); + } else { + $result = str_replace('DEFAULT NULL', 'NULL', $result); + } + } elseif (array_keys($column) == array('type', 'name')) { + $result .= ' NULL'; + } elseif (strpos($result, "DEFAULT N'")) { + $result = str_replace("DEFAULT N'", "DEFAULT '", $result); + } + return $result; + } + +/** + * Format indexes for create table + * + * @param array $indexes + * @param string $table + * @return string + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + + foreach ($indexes as $name => $value) { + if ($name == 'PRIMARY') { + $join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')'; + } else if (isset($value['unique']) && $value['unique']) { + $out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE"; + + if (is_array($value['column'])) { + $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column'])); + } else { + $value['column'] = $this->name($value['column']); + } + $out .= "({$value['column']});"; + $join[] = $out; + } + } + return $join; + } + +/** + * Makes sure it will return the primary key + * + * @param mixed $model Model instance of table name + * @return string + */ + protected function _getPrimaryKey($model) { + if (!is_object($model)) { + $model = new Model(false, $model); + } + $schema = $this->describe($model); + foreach ($schema as $field => $props) { + if (isset($props['key']) && $props['key'] == 'primary') { + return $field; + } + } + return null; + } + +/** + * Returns number of affected rows in previous database operation. If no previous operation exists, + * this returns false. + * + * @param mixed $source + * @return integer Number of affected rows + */ + public function lastAffected($source = null) { + $affected = parent::lastAffected(); + if ($affected === null && $this->_lastAffected !== false) { + return $this->_lastAffected; + } + return $affected; + } +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @param array $params list of params to be bound to query (supported only in select) + * @param array $prepareOptions Options to be used in the prepare statement + * @return mixed PDOStatement if query executes with no problem, true as the result of a succesfull, false on error + * query returning no rows, suchs as a CREATE statement, false otherwise + */ + protected function _execute($sql, $params = array(), $prepareOptions = array()) { + $this->_lastAffected = false; + if (strncasecmp($sql, 'SELECT', 6) == 0) { + $prepareOptions += array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL); + return parent::_execute($sql, $params, $prepareOptions); + } + try { + $this->_lastAffected = $this->_connection->exec($sql); + if ($this->_lastAffected === false) { + $this->_results = null; + $error = $this->_connection->errorInfo(); + $this->error = $error[2]; + return false; + } + return true; + } catch (PDOException $e) { + $this->_results = null; + $this->error = $e->getMessage(); + return false; + } + } + +/** + * Generate a "drop table" statement for the given Schema object + * + * @param CakeSchema $schema An instance of a subclass of CakeSchema + * @param string $table Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + public function dropSchema(CakeSchema $schema, $table = null) { + $out = ''; + foreach ($schema->tables as $curTable => $columns) { + if (!$table || $table == $curTable) { + $t = $this->fullTableName($curTable); + $out .= "IF OBJECT_ID('" . $this->fullTableName($curTable, false). "', 'U') IS NOT NULL DROP TABLE " . $this->fullTableName($curTable) . ";\n"; + } + } + return $out; + } + +} diff --git a/app/Cake/Model/Datasource/DboSource.php b/app/Cake/Model/Datasource/DboSource.php new file mode 100644 index 00000000..add88cd9 --- /dev/null +++ b/app/Cake/Model/Datasource/DboSource.php @@ -0,0 +1,3087 @@ + 'primary', 'MUL' => 'index', 'UNI' => 'unique'); + +/** + * Database keyword used to assign aliases to identifiers. + * + * @var string + */ + public $alias = 'AS '; + +/** + * Caches result from query parsing operations. Cached results for both DboSource::name() and + * DboSource::conditions() will be stored here. Method caching uses `crc32()` which is + * fast but can collisions more easily than other hashing algorithms. If you have problems + * with collisions, set DboSource::$cacheMethods to false. + * + * @var array + */ + public static $methodCache = array(); + +/** + * Whether or not to cache the results of DboSource::name() and DboSource::conditions() + * into the memory cache. Set to false to disable the use of the memory cache. + * + * @var boolean. + */ + public $cacheMethods = true; + +/** + * Print full query debug info? + * + * @var boolean + */ + public $fullDebug = false; + +/** + * String to hold how many rows were affected by the last SQL operation. + * + * @var string + */ + public $affected = null; + +/** + * Number of rows in current resultset + * + * @var integer + */ + public $numRows = null; + +/** + * Time the last query took + * + * @var integer + */ + public $took = null; + +/** + * Result + * + * @var array + */ + protected $_result = null; + +/** + * Queries count. + * + * @var integer + */ + protected $_queriesCnt = 0; + +/** + * Total duration of all queries. + * + * @var integer + */ + protected $_queriesTime = null; + +/** + * Log of queries executed by this DataSource + * + * @var array + */ + protected $_queriesLog = array(); + +/** + * Maximum number of items in query log + * + * This is to prevent query log taking over too much memory. + * + * @var integer Maximum number of queries in the queries log. + */ + protected $_queriesLogMax = 200; + +/** + * Caches serialzed results of executed queries + * + * @var array Maximum number of queries in the queries log. + */ + protected $_queryCache = array(); + +/** + * A reference to the physical connection of this DataSource + * + * @var array + */ + protected $_connection = null; + +/** + * The DataSource configuration key name + * + * @var string + */ + public $configKeyName = null; + +/** + * The starting character that this DataSource uses for quoted identifiers. + * + * @var string + */ + public $startQuote = null; + +/** + * The ending character that this DataSource uses for quoted identifiers. + * + * @var string + */ + public $endQuote = null; + +/** + * The set of valid SQL operations usable in a WHERE statement + * + * @var array + */ + protected $_sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to'); + +/** + * Indicates the level of nested transactions + * + * @var integer + */ + protected $_transactionNesting = 0; + +/** + * Index of basic SQL commands + * + * @var array + */ + protected $_commands = array( + 'begin' => 'BEGIN', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); + +/** + * Separator string for virtualField composition + * + * @var string + */ + public $virtualFieldSeparator = '__'; + +/** + * List of table engine specific parameters used on table creating + * + * @var array + */ + public $tableParameters = array(); + +/** + * List of engine specific additional field parameters used on table creating + * + * @var array + */ + public $fieldParameters = array(); + +/** + * Indicates whether there was a change on the cached results on the methods of this class + * This will be used for storing in a more persistent cache + * + * @var boolean + */ + protected $_methodCacheChange = false; + +/** + * Constructor + * + * @param array $config Array of configuration information for the Datasource. + * @param boolean $autoConnect Whether or not the datasource should automatically connect. + */ + public function __construct($config = null, $autoConnect = true) { + if (!isset($config['prefix'])) { + $config['prefix'] = ''; + } + parent::__construct($config); + $this->fullDebug = Configure::read('debug') > 1; + if (!$this->enabled()) { + throw new MissingConnectionException(array( + 'class' => get_class($this) + )); + } + if ($autoConnect) { + $this->connect(); + } + } + +/** + * Reconnects to database server with optional new settings + * + * @param array $config An array defining the new configuration settings + * @return boolean True on success, false on failure + */ + public function reconnect($config = array()) { + $this->disconnect(); + $this->setConfig($config); + $this->_sources = null; + + return $this->connect(); + } + +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + public function disconnect() { + if ($this->_result instanceof PDOStatement) { + $this->_result->closeCursor(); + } + unset($this->_connection); + $this->connected = false; + return true; + } + +/** + * Get the underlying connection object. + * + * @return PDOConnection + */ + public function getConnection() { + return $this->_connection; + } + +/** + * Returns a quoted and escaped string of $data for use in an SQL statement. + * + * @param string $data String to be prepared for use in an SQL statement + * @param string $column The column into which this data will be inserted + * @return string Quoted and escaped data + */ + public function value($data, $column = null) { + if (is_array($data) && !empty($data)) { + return array_map( + array(&$this, 'value'), + $data, array_fill(0, count($data), $column) + ); + } elseif (is_object($data) && isset($data->type, $data->value)) { + if ($data->type == 'identifier') { + return $this->name($data->value); + } elseif ($data->type == 'expression') { + return $data->value; + } + } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) { + return $data; + } + + if ($data === null || (is_array($data) && empty($data))) { + return 'NULL'; + } + + if (empty($column)) { + $column = $this->introspectType($data); + } + + switch ($column) { + case 'binary': + return $this->_connection->quote($data, PDO::PARAM_LOB); + break; + case 'boolean': + return $this->_connection->quote($this->boolean($data, true), PDO::PARAM_BOOL); + break; + case 'string': + case 'text': + return $this->_connection->quote($data, PDO::PARAM_STR); + default: + if ($data === '') { + return 'NULL'; + } + if (is_float($data)) { + return sprintf('%F', $data); + } + if ((is_int($data) || $data === '0') || ( + is_numeric($data) && strpos($data, ',') === false && + $data[0] != '0' && strpos($data, 'e') === false) + ) { + return $data; + } + return $this->_connection->quote($data); + break; + } + } + + +/** + * Returns an object to represent a database identifier in a query. Expression objects + * are not sanitized or esacped. + * + * @param string $identifier A SQL expression to be used as an identifier + * @return stdClass An object representing a database identifier to be used in a query + */ + public function identifier($identifier) { + $obj = new stdClass(); + $obj->type = 'identifier'; + $obj->value = $identifier; + return $obj; + } + +/** + * Returns an object to represent a database expression in a query. Expression objects + * are not sanitized or esacped. + * + * @param string $expression An arbitrary SQL expression to be inserted into a query. + * @return stdClass An object representing a database expression to be used in a query + */ + public function expression($expression) { + $obj = new stdClass(); + $obj->type = 'expression'; + $obj->value = $expression; + return $obj; + } + +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @param array $params Additional options for the query. + * @return boolean + */ + public function rawQuery($sql, $params = array()) { + $this->took = $this->numRows = false; + return $this->execute($sql, $params); + } + +/** + * Queries the database with given SQL statement, and obtains some metadata about the result + * (rows affected, timing, any errors, number of rows in resultset). The query is also logged. + * If Configure::read('debug') is set, the log is shown all the time, else it is only shown on errors. + * + * ### Options + * + * - log - Whether or not the query should be logged to the memory log. + * + * @param string $sql + * @param array $options + * @param array $params values to be bided to the query + * @return mixed Resource or object representing the result set, or false on failure + */ + public function execute($sql, $options = array(), $params = array()) { + $options += array('log' => $this->fullDebug); + + $t = microtime(true); + $this->_result = $this->_execute($sql, $params); + + if ($options['log']) { + $this->took = round((microtime(true) - $t) * 1000, 0); + $this->numRows = $this->affected = $this->lastAffected(); + $this->logQuery($sql); + } + + return $this->_result; + } + +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @param array $params list of params to be bound to query + * @param array $prepareOptions Options to be used in the prepare statement + * @return mixed PDOStatement if query executes with no problem, true as the result of a succesfull, false on error + * query returning no rows, suchs as a CREATE statement, false otherwise + */ + protected function _execute($sql, $params = array(), $prepareOptions = array()) { + $sql = trim($sql); + if (preg_match('/^(?:CREATE|ALTER|DROP)/i', $sql)) { + $statements = array_filter(explode(';', $sql)); + if (count($statements) > 1) { + $result = array_map(array($this, '_execute'), $statements); + return array_search(false, $result) === false; + } + } + + try { + $query = $this->_connection->prepare($sql, $prepareOptions); + $query->setFetchMode(PDO::FETCH_LAZY); + if (!$query->execute($params)) { + $this->_results = $query; + $query->closeCursor(); + return false; + } + if (!$query->columnCount()) { + $query->closeCursor(); + return true; + } + return $query; + } catch (PDOException $e) { + if (isset($query->queryString)) { + $e->queryString = $query->queryString; + } else { + $e->queryString = $sql; + } + throw $e; + } + } + +/** + * Returns a formatted error message from previous database operation. + * + * @param PDOStatement $query the query to extract the error from if any + * @return string Error message with error number + */ + public function lastError(PDOStatement $query = null) { + $error = $query->errorInfo(); + if (empty($error[2])) { + return null; + } + return $error[1] . ': ' . $error[2]; + } + +/** + * Returns number of affected rows in previous database operation. If no previous operation exists, + * this returns false. + * + * @param mixed $source + * @return integer Number of affected rows + */ + public function lastAffected($source = null) { + if ($this->hasResult()) { + return $this->_result->rowCount(); + } + return null; + } + +/** + * Returns number of rows in previous resultset. If no previous resultset exists, + * this returns false. + * + * @param mixed $source Not used + * @return integer Number of rows in resultset + */ + public function lastNumRows($source = null) { + return $this->lastAffected(); + } + +/** + * DataSource Query abstraction + * + * @return resource Result resource identifier. + */ + public function query() { + $args = func_get_args(); + $fields = null; + $order = null; + $limit = null; + $page = null; + $recursive = null; + + if (count($args) === 1) { + return $this->fetchAll($args[0]); + } elseif (count($args) > 1 && (strpos($args[0], 'findBy') === 0 || strpos($args[0], 'findAllBy') === 0)) { + $params = $args[1]; + + if (substr($args[0], 0, 6) === 'findBy') { + $all = false; + $field = Inflector::underscore(substr($args[0], 6)); + } else { + $all = true; + $field = Inflector::underscore(substr($args[0], 9)); + } + + $or = (strpos($field, '_or_') !== false); + if ($or) { + $field = explode('_or_', $field); + } else { + $field = explode('_and_', $field); + } + $off = count($field) - 1; + + if (isset($params[1 + $off])) { + $fields = $params[1 + $off]; + } + + if (isset($params[2 + $off])) { + $order = $params[2 + $off]; + } + + if (!array_key_exists(0, $params)) { + return false; + } + + $c = 0; + $conditions = array(); + + foreach ($field as $f) { + $conditions[$args[2]->alias . '.' . $f] = $params[$c++]; + } + + if ($or) { + $conditions = array('OR' => $conditions); + } + + if ($all) { + if (isset($params[3 + $off])) { + $limit = $params[3 + $off]; + } + + if (isset($params[4 + $off])) { + $page = $params[4 + $off]; + } + + if (isset($params[5 + $off])) { + $recursive = $params[5 + $off]; + } + return $args[2]->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } else { + if (isset($params[3 + $off])) { + $recursive = $params[3 + $off]; + } + return $args[2]->find('first', compact('conditions', 'fields', 'order', 'recursive')); + } + } else { + if (isset($args[1]) && $args[1] === true) { + return $this->fetchAll($args[0], true); + } else if (isset($args[1]) && !is_array($args[1]) ) { + return $this->fetchAll($args[0], false); + } else if (isset($args[1]) && is_array($args[1])) { + $offset = 0; + if (isset($args[2])) { + $cache = $args[2]; + } else { + $cache = true; + } + return $this->fetchAll($args[0], $args[1], array('cache' => $cache)); + } + } + } + +/** + * Returns a row from current resultset as an array + * + * @param string $sql Some SQL to be executed. + * @return array The fetched row as an array + */ + public function fetchRow($sql = null) { + if (is_string($sql) && strlen($sql) > 5 && !$this->execute($sql)) { + return null; + } + + if ($this->hasResult()) { + $this->resultSet($this->_result); + $resultRow = $this->fetchResult(); + if (isset($resultRow[0])) { + $this->fetchVirtualField($resultRow); + } + return $resultRow; + } else { + return null; + } + } + +/** + * Returns an array of all result rows for a given SQL query. + * Returns false if no rows matched. + * + * + * ### Options + * + * - `cache` - Returns the cached version of the query, if exists and stores the result in cache. + * This is a non-persistent cache, and only lasts for a single request. This option + * defaults to true. If you are directly calling this method, you can disable caching + * by setting $options to `false` + * + * @param string $sql SQL statement + * @param array $params parameters to be bound as values for the SQL statement + * @param array $options additional options for the query. + * @return array Array of resultset rows, or false if no rows matched + */ + public function fetchAll($sql, $params = array(), $options = array()) { + if (is_string($options)) { + $options = array('modelName' => $options); + } + if (is_bool($params)) { + $options['cache'] = $params; + $params = array(); + } + $options += array('cache' => true); + $cache = $options['cache']; + if ($cache && ($cached = $this->getQueryCache($sql, $params)) !== false) { + return $cached; + } + if ($result = $this->execute($sql, array(), $params)) { + $out = array(); + + if ($this->hasResult()) { + $first = $this->fetchRow(); + if ($first != null) { + $out[] = $first; + } + while ($item = $this->fetchResult()) { + if (isset($item[0])) { + $this->fetchVirtualField($item); + } + $out[] = $item; + } + } + + if (!is_bool($result) && $cache) { + $this->_writeQueryCache($sql, $out, $params); + } + + if (empty($out) && is_bool($this->_result)) { + return $this->_result; + } + return $out; + } + return false; + } + +/** + * Fetches the next row from the current result set + * + * @return boolean + */ + public function fetchResult() { + return false; + } + +/** + * Modifies $result array to place virtual fields in model entry where they belongs to + * + * @param array $result Reference to the fetched row + * @return void + */ + public function fetchVirtualField(&$result) { + if (isset($result[0]) && is_array($result[0])) { + foreach ($result[0] as $field => $value) { + if (strpos($field, $this->virtualFieldSeparator) === false) { + continue; + } + list($alias, $virtual) = explode($this->virtualFieldSeparator, $field); + + if (!ClassRegistry::isKeySet($alias)) { + return; + } + $model = ClassRegistry::getObject($alias); + if ($model->isVirtualField($virtual)) { + $result[$alias][$virtual] = $value; + unset($result[0][$field]); + } + } + if (empty($result[0])) { + unset($result[0]); + } + } + } + +/** + * Returns a single field of the first of query results for a given SQL query, or false if empty. + * + * @param string $name Name of the field + * @param string $sql SQL query + * @return mixed Value of field read. + */ + public function field($name, $sql) { + $data = $this->fetchRow($sql); + if (empty($data[$name])) { + return false; + } + return $data[$name]; + } + +/** + * Empties the method caches. + * These caches are used by DboSource::name() and DboSource::conditions() + * + * @return void + */ + public function flushMethodCache() { + $this->_methodCacheChange = true; + self::$methodCache = array(); + } + +/** + * Cache a value into the methodCaches. Will respect the value of DboSource::$cacheMethods. + * Will retrieve a value from the cache if $value is null. + * + * If caching is disabled and a write is attempted, the $value will be returned. + * A read will either return the value or null. + * + * @param string $method Name of the method being cached. + * @param string $key The keyname for the cache operation. + * @param mixed $value The value to cache into memory. + * @return mixed Either null on failure, or the value if its set. + */ + public function cacheMethod($method, $key, $value = null) { + if ($this->cacheMethods === false) { + return $value; + } + if (empty(self::$methodCache)) { + self::$methodCache = Cache::read('method_cache', '_cake_core_'); + } + if ($value === null) { + return (isset(self::$methodCache[$method][$key])) ? self::$methodCache[$method][$key] : null; + } + $this->_methodCacheChange = true; + return self::$methodCache[$method][$key] = $value; + } + +/** + * Returns a quoted name of $data for use in an SQL statement. + * Strips fields out of SQL functions before quoting. + * + * Results of this method are stored in a memory cache. This improves performance, but + * because the method uses a simple hashing algorithm it can infrequently have collisions. + * Setting DboSource::$cacheMethods to false will disable the memory cache. + * + * @param mixed $data Either a string with a column to quote. An array of columns to quote or an + * object from DboSource::expression() or DboSource::identifier() + * @return string SQL field + */ + public function name($data) { + if (is_object($data) && isset($data->type)) { + return $data->value; + } + if ($data === '*') { + return '*'; + } + if (is_array($data)) { + foreach ($data as $i => $dataItem) { + $data[$i] = $this->name($dataItem); + } + return $data; + } + $cacheKey = crc32($this->startQuote.$data.$this->endQuote); + if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) { + return $return; + } + $data = trim($data); + if (preg_match('/^[\w-]+(?:\.[^ \*]*)*$/', $data)) { // string, string.string + if (strpos($data, '.') === false) { // string + return $this->cacheMethod(__FUNCTION__, $cacheKey, $this->startQuote . $data . $this->endQuote); + } + $items = explode('.', $data); + return $this->cacheMethod(__FUNCTION__, $cacheKey, + $this->startQuote . implode($this->endQuote . '.' . $this->startQuote, $items) . $this->endQuote + ); + } + if (preg_match('/^[\w-]+\.\*$/', $data)) { // string.* + return $this->cacheMethod(__FUNCTION__, $cacheKey, + $this->startQuote . str_replace('.*', $this->endQuote . '.*', $data) + ); + } + if (preg_match('/^([\w-]+)\((.*)\)$/', $data, $matches)) { // Functions + return $this->cacheMethod(__FUNCTION__, $cacheKey, + $matches[1] . '(' . $this->name($matches[2]) . ')' + ); + } + if ( + preg_match('/^([\w-]+(\.[\w-]+|\(.*\))*)\s+' . preg_quote($this->alias) . '\s*([\w-]+)$/i', $data, $matches + )) { + return $this->cacheMethod( + __FUNCTION__, $cacheKey, + preg_replace( + '/\s{2,}/', ' ', $this->name($matches[1]) . ' ' . $this->alias . ' ' . $this->name($matches[3]) + ) + ); + } + if (preg_match('/^[\w-_\s]*[\w-_]+/', $data)) { + return $this->cacheMethod(__FUNCTION__, $cacheKey, $this->startQuote . $data . $this->endQuote); + } + return $this->cacheMethod(__FUNCTION__, $cacheKey, $data); + } + +/** + * Checks if the source is connected to the database. + * + * @return boolean True if the database is connected, else false + */ + public function isConnected() { + return $this->connected; + } + +/** + * Checks if the result is valid + * + * @return boolean True if the result is valid else false + */ + public function hasResult() { + return is_a($this->_result, 'PDOStatement'); + } + +/** + * Get the query log as an array. + * + * @param boolean $sorted Get the queries sorted by time taken, defaults to false. + * @param boolean $clear If True the existing log will cleared. + * @return array Array of queries run as an array + */ + public function getLog($sorted = false, $clear = true) { + if ($sorted) { + $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC); + } else { + $log = $this->_queriesLog; + } + if ($clear) { + $this->_queriesLog = array(); + } + return array('log' => $log, 'count' => $this->_queriesCnt, 'time' => $this->_queriesTime); + } + +/** + * Outputs the contents of the queries log. If in a non-CLI environment the sql_log element + * will be rendered and output. If in a CLI environment, a plain text log is generated. + * + * @param boolean $sorted Get the queries sorted by time taken, defaults to false. + * @return void + */ + public function showLog($sorted = false) { + $log = $this->getLog($sorted, false); + if (empty($log['log'])) { + return; + } + if (PHP_SAPI != 'cli') { + $controller = null; + $View = new View($controller, false); + $View->set('logs', array($this->configKeyName => $log)); + echo $View->element('sql_dump', array('_forced_from_dbo_' => true)); + } else { + foreach ($log['log'] as $k => $i) { + print (($k + 1) . ". {$i['query']}\n"); + } + } + } + +/** + * Log given SQL query. + * + * @param string $sql SQL statement + * @return void + */ + public function logQuery($sql) { + $this->_queriesCnt++; + $this->_queriesTime += $this->took; + $this->_queriesLog[] = array( + 'query' => $sql, + 'affected' => $this->affected, + 'numRows' => $this->numRows, + 'took' => $this->took + ); + if (count($this->_queriesLog) > $this->_queriesLogMax) { + array_pop($this->_queriesLog); + } + } + +/** + * Gets full table name including prefix + * + * @param mixed $model Either a Model object or a string table name. + * @param boolean $quote Whether you want the table name quoted. + * @return string Full quoted table name + */ + public function fullTableName($model, $quote = true) { + if (is_object($model)) { + $table = $model->tablePrefix . $model->table; + } elseif (isset($this->config['prefix'])) { + $table = $this->config['prefix'] . strval($model); + } else { + $table = strval($model); + } + if ($quote) { + return $this->name($table); + } + return $table; + } + +/** + * The "C" in CRUD + * + * Creates new records in the database. + * + * @param Model $model Model object that the record is for. + * @param array $fields An array of field names to insert. If null, $model->data will be + * used to generate field names. + * @param array $values An array of values with keys matching the fields. If null, $model->data will + * be used to generate values. + * @return boolean Success + */ + public function create(Model $model, $fields = null, $values = null) { + $id = null; + + if ($fields == null) { + unset($fields, $values); + $fields = array_keys($model->data); + $values = array_values($model->data); + } + $count = count($fields); + + for ($i = 0; $i < $count; $i++) { + $valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i])); + } + for ($i = 0; $i < $count; $i++) { + $fieldInsert[] = $this->name($fields[$i]); + if ($fields[$i] == $model->primaryKey) { + $id = $values[$i]; + } + } + $query = array( + 'table' => $this->fullTableName($model), + 'fields' => implode(', ', $fieldInsert), + 'values' => implode(', ', $valueInsert) + ); + + if ($this->execute($this->renderStatement('create', $query))) { + if (empty($id)) { + $id = $this->lastInsertId($this->fullTableName($model, false), $model->primaryKey); + } + $model->setInsertID($id); + $model->id = $id; + return true; + } + $model->onError(); + return false; + } + +/** + * The "R" in CRUD + * + * Reads record(s) from the database. + * + * @param Model $model A Model object that the query is for. + * @param array $queryData An array of queryData information containing keys similar to Model::find() + * @param integer $recursive Number of levels of association + * @return mixed boolean false on error/failure. An array of results on success. + */ + public function read(Model $model, $queryData = array(), $recursive = null) { + $queryData = $this->_scrubQueryData($queryData); + + $null = null; + $array = array(); + $linkedModels = array(); + $bypass = false; + + if ($recursive === null && isset($queryData['recursive'])) { + $recursive = $queryData['recursive']; + } + + if (!is_null($recursive)) { + $_recursive = $model->recursive; + $model->recursive = $recursive; + } + + if (!empty($queryData['fields'])) { + $bypass = true; + $queryData['fields'] = $this->fields($model, null, $queryData['fields']); + } else { + $queryData['fields'] = $this->fields($model); + } + + $_associations = $model->associations(); + + if ($model->recursive == -1) { + $_associations = array(); + } elseif ($model->recursive == 0) { + unset($_associations[2], $_associations[3]); + } + + foreach ($_associations as $type) { + foreach ($model->{$type} as $assoc => $assocData) { + $linkModel = $model->{$assoc}; + $external = isset($assocData['external']); + + $linkModel->getDataSource(); + if ($model->useDbConfig === $linkModel->useDbConfig) { + if ($bypass) { + $assocData['fields'] = false; + } + if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) { + $linkedModels[$type . '/' . $assoc] = true; + } + } + } + } + + $query = trim($this->generateAssociationQuery($model, null, null, null, null, $queryData, false, $null)); + + $resultSet = $this->fetchAll($query, $model->cacheQueries); + if ($resultSet === false) { + $model->onError(); + return false; + } + + $filtered = $this->_filterResults($resultSet, $model); + + if ($model->recursive > -1) { + foreach ($_associations as $type) { + foreach ($model->{$type} as $assoc => $assocData) { + $linkModel = $model->{$assoc}; + + if (!isset($linkedModels[$type . '/' . $assoc])) { + if ($model->useDbConfig === $linkModel->useDbConfig) { + $db = $this; + } else { + $db = ConnectionManager::getDataSource($linkModel->useDbConfig); + } + } elseif ($model->recursive > 1 && ($type === 'belongsTo' || $type === 'hasOne')) { + $db = $this; + } + + if (isset($db) && method_exists($db, 'queryAssociation')) { + $stack = array($assoc); + $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack); + unset($db); + + if ($type === 'hasMany') { + $filtered[] = $assoc; + } + } + } + } + $this->_filterResults($resultSet, $model, $filtered); + } + + if (!is_null($recursive)) { + $model->recursive = $_recursive; + } + return $resultSet; + } + +/** + * Passes association results thru afterFind filters of corresponding model + * + * @param array $results Reference of resultset to be filtered + * @param Model $model Instance of model to operate against + * @param array $filtered List of classes already filtered, to be skipped + * @return array Array of results that have been filtered through $model->afterFind + */ + protected function _filterResults(&$results, Model $model, $filtered = array()) { + $current = current($results); + if (!is_array($current)) { + return array(); + } + $keys = array_diff(array_keys($current), $filtered, array($model->alias)); + $filtering = array(); + foreach ($keys as $className) { + if (!isset($model->{$className}) || !is_object($model->{$className})) { + continue; + } + $linkedModel = $model->{$className}; + $filtering[] = $className; + foreach ($results as &$result) { + $data = $linkedModel->afterFind(array(array($className => $result[$className])), false); + if (isset($data[0][$className])) { + $result[$className] = $data[0][$className]; + } + } + } + return $filtering; + } + +/** + * Queries associations. Used to fetch results on recursive models. + * + * @param Model $model Primary Model object + * @param Model $linkModel Linked model that + * @param string $type Association type, one of the model association types ie. hasMany + * @param string $association + * @param array $assocData + * @param array $queryData + * @param boolean $external Whether or not the association query is on an external datasource. + * @param array $resultSet Existing results + * @param integer $recursive Number of levels of association + * @param array $stack + * @return mixed + */ + public function queryAssociation($model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) { + if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) { + if (!is_array($resultSet)) { + throw new CakeException(__d('cake_dev', 'Error in Model %s', get_class($model))); + } + if ($type === 'hasMany' && empty($assocData['limit']) && !empty($assocData['foreignKey'])) { + $ins = $fetch = array(); + foreach ($resultSet as &$result) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $result, $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + + if (!empty($ins)) { + $ins = array_unique($ins); + $fetch = $this->fetchAssociated($model, $query, $ins); + } + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + foreach ($linkModel->associations() as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + $deepModel = $linkModel->{$assoc1}; + $tmpStack = $stack; + $tmpStack[] = $assoc1; + + if ($linkModel->useDbConfig === $deepModel->useDbConfig) { + $db = $this; + } else { + $db = ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + $this->_filterResults($fetch, $model); + return $this->_mergeHasMany($resultSet, $fetch, $association, $model, $linkModel); + } elseif ($type === 'hasAndBelongsToMany') { + $ins = $fetch = array(); + foreach ($resultSet as &$result) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $result, $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + if (!empty($ins)) { + $ins = array_unique($ins); + if (count($ins) > 1) { + $query = str_replace('{$__cakeID__$}', '(' .implode(', ', $ins) .')', $query); + $query = str_replace('= (', 'IN (', $query); + } else { + $query = str_replace('{$__cakeID__$}', $ins[0], $query); + } + + $query = str_replace(' WHERE 1 = 1', '', $query); + } + + $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey']; + $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']); + list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys); + $habtmFieldsCount = count($habtmFields); + $q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack); + + if ($q !== false) { + $fetch = $this->fetchAll($q, $model->cacheQueries); + } else { + $fetch = null; + } + } + + $modelAlias = $model->alias; + $modelPK = $model->primaryKey; + foreach ($resultSet as &$row) { + if ($type !== 'hasAndBelongsToMany') { + $q = $this->insertQueryData($query, $row, $association, $assocData, $model, $linkModel, $stack); + if ($q !== false) { + $fetch = $this->fetchAll($q, $model->cacheQueries); + } else { + $fetch = null; + } + } + $selfJoin = $linkModel->name === $model->name; + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + foreach ($linkModel->associations() as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + $deepModel = $linkModel->{$assoc1}; + + if ($type1 === 'belongsTo' || ($deepModel->alias === $modelAlias && $type === 'belongsTo') || ($deepModel->alias !== $modelAlias)) { + $tmpStack = $stack; + $tmpStack[] = $assoc1; + if ($linkModel->useDbConfig == $deepModel->useDbConfig) { + $db = $this; + } else { + $db = ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + if ($type === 'hasAndBelongsToMany') { + $uniqueIds = $merge = array(); + + foreach ($fetch as $j => $data) { + if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$modelAlias][$modelPK]) { + if ($habtmFieldsCount <= 2) { + unset($data[$with]); + } + $merge[] = $data; + } + } + if (empty($merge) && !isset($row[$association])) { + $row[$association] = $merge; + } else { + $this->_mergeAssociation($row, $merge, $association, $type); + } + } else { + $this->_mergeAssociation($row, $fetch, $association, $type, $selfJoin); + } + if (isset($row[$association])) { + $row[$association] = $linkModel->afterFind($row[$association], false); + } + } else { + $tempArray[0][$association] = false; + $this->_mergeAssociation($row, $tempArray, $association, $type, $selfJoin); + } + } + } + } + +/** + * A more efficient way to fetch associations. Woohoo! + * + * @param Model $model Primary model object + * @param string $query Association query + * @param array $ids Array of IDs of associated records + * @return array Association results + */ + public function fetchAssociated($model, $query, $ids) { + $query = str_replace('{$__cakeID__$}', implode(', ', $ids), $query); + if (count($ids) > 1) { + $query = str_replace('= (', 'IN (', $query); + } + return $this->fetchAll($query, $model->cacheQueries); + } + +/** + * mergeHasMany - Merge the results of hasMany relations. + * + * + * @param array $resultSet Data to merge into + * @param array $merge Data to merge + * @param string $association Name of Model being Merged + * @param Model $model Model being merged onto + * @param Model $linkModel Model being merged + * @return void + */ + protected function _mergeHasMany(&$resultSet, $merge, $association, $model, $linkModel) { + $modelAlias = $model->alias; + $modelPK = $model->primaryKey; + $modelFK = $model->hasMany[$association]['foreignKey']; + foreach ($resultSet as &$result) { + if (!isset($result[$modelAlias])) { + continue; + } + $merged = array(); + foreach ($merge as $data) { + if ($result[$modelAlias][$modelPK] === $data[$association][$modelFK]) { + if (count($data) > 1) { + $data = array_merge($data[$association], $data); + unset($data[$association]); + foreach ($data as $key => $name) { + if (is_numeric($key)) { + $data[$association][] = $name; + unset($data[$key]); + } + } + $merged[] = $data; + } else { + $merged[] = $data[$association]; + } + } + } + $result = Set::pushDiff($result, array($association => $merged)); + } + } + +/** + * Merge association of merge into data + * + * @param array $data + * @param array $merge + * @param string $association + * @param string $type + * @param boolean $selfJoin + * @return void + */ + protected function _mergeAssociation(&$data, &$merge, $association, $type, $selfJoin = false) { + if (isset($merge[0]) && !isset($merge[0][$association])) { + $association = Inflector::pluralize($association); + } + + if ($type === 'belongsTo' || $type === 'hasOne') { + if (isset($merge[$association])) { + $data[$association] = $merge[$association][0]; + } else { + if (count($merge[0][$association]) > 1) { + foreach ($merge[0] as $assoc => $data2) { + if ($assoc !== $association) { + $merge[0][$association][$assoc] = $data2; + } + } + } + if (!isset($data[$association])) { + if ($merge[0][$association] != null) { + $data[$association] = $merge[0][$association]; + } else { + $data[$association] = array(); + } + } else { + if (is_array($merge[0][$association])) { + foreach ($data[$association] as $k => $v) { + if (!is_array($v)) { + $dataAssocTmp[$k] = $v; + } + } + + foreach ($merge[0][$association] as $k => $v) { + if (!is_array($v)) { + $mergeAssocTmp[$k] = $v; + } + } + $dataKeys = array_keys($data); + $mergeKeys = array_keys($merge[0]); + + if ($mergeKeys[0] === $dataKeys[0] || $mergeKeys === $dataKeys) { + $data[$association][$association] = $merge[0][$association]; + } else { + $diff = Set::diff($dataAssocTmp, $mergeAssocTmp); + $data[$association] = array_merge($merge[0][$association], $diff); + } + } elseif ($selfJoin && array_key_exists($association, $merge[0])) { + $data[$association] = array_merge($data[$association], array($association => array())); + } + } + } + } else { + if (isset($merge[0][$association]) && $merge[0][$association] === false) { + if (!isset($data[$association])) { + $data[$association] = array(); + } + } else { + foreach ($merge as $i => $row) { + if (count($row) === 1) { + if (empty($data[$association]) || (isset($data[$association]) && !in_array($row[$association], $data[$association]))) { + $data[$association][] = $row[$association]; + } + } elseif (!empty($row)) { + $tmp = array_merge($row[$association], $row); + unset($tmp[$association]); + $data[$association][] = $tmp; + } + } + } + } + } + +/** + * Generates an array representing a query or part of a query from a single model or two associated models + * + * @param Model $model + * @param Model $linkModel + * @param string $type + * @param string $association + * @param array $assocData + * @param array $queryData + * @param boolean $external + * @param array $resultSet + * @return mixed + */ + public function generateAssociationQuery($model, $linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) { + $queryData = $this->_scrubQueryData($queryData); + $assocData = $this->_scrubQueryData($assocData); + $modelAlias = $model->alias; + + if (empty($queryData['fields'])) { + $queryData['fields'] = $this->fields($model, $modelAlias); + } elseif (!empty($model->hasMany) && $model->recursive > -1) { + $assocFields = $this->fields($model, $modelAlias, array("{$modelAlias}.{$model->primaryKey}")); + $passedFields = $queryData['fields']; + if (count($passedFields) === 1) { + if (strpos($passedFields[0], $assocFields[0]) === false && !preg_match('/^[a-z]+\(/i', $passedFields[0])) { + $queryData['fields'] = array_merge($passedFields, $assocFields); + } else { + $queryData['fields'] = $passedFields; + } + } else { + $queryData['fields'] = array_merge($passedFields, $assocFields); + } + unset($assocFields, $passedFields); + } + + if ($linkModel === null) { + return $this->buildStatement( + array( + 'fields' => array_unique($queryData['fields']), + 'table' => $this->fullTableName($model), + 'alias' => $modelAlias, + 'limit' => $queryData['limit'], + 'offset' => $queryData['offset'], + 'joins' => $queryData['joins'], + 'conditions' => $queryData['conditions'], + 'order' => $queryData['order'], + 'group' => $queryData['group'] + ), + $model + ); + } + if ($external && !empty($assocData['finderQuery'])) { + return $assocData['finderQuery']; + } + + $self = $model->name === $linkModel->name; + $fields = array(); + + if ($external || (in_array($type, array('hasOne', 'belongsTo')) && $assocData['fields'] !== false)) { + $fields = $this->fields($linkModel, $association, $assocData['fields']); + } + if (empty($assocData['offset']) && !empty($assocData['page'])) { + $assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit']; + } + $assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']); + + switch ($type) { + case 'hasOne': + case 'belongsTo': + $conditions = $this->_mergeConditions( + $assocData['conditions'], + $this->getConstraint($type, $model, $linkModel, $association, array_merge($assocData, compact('external', 'self'))) + ); + + if (!$self && $external) { + foreach ($conditions as $key => $condition) { + if (is_numeric($key) && strpos($condition, $modelAlias . '.') !== false) { + unset($conditions[$key]); + } + } + } + + if ($external) { + $query = array_merge($assocData, array( + 'conditions' => $conditions, + 'table' => $this->fullTableName($linkModel), + 'fields' => $fields, + 'alias' => $association, + 'group' => null + )); + $query += array('order' => $assocData['order'], 'limit' => $assocData['limit']); + } else { + $join = array( + 'table' => $this->fullTableName($linkModel), + 'alias' => $association, + 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', + 'conditions' => trim($this->conditions($conditions, true, false, $model)) + ); + $queryData['fields'] = array_merge($queryData['fields'], $fields); + + if (!empty($assocData['order'])) { + $queryData['order'][] = $assocData['order']; + } + if (!in_array($join, $queryData['joins'])) { + $queryData['joins'][] = $join; + } + return true; + } + break; + case 'hasMany': + $assocData['fields'] = $this->fields($linkModel, $association, $assocData['fields']); + if (!empty($assocData['foreignKey'])) { + $assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $association, array("{$association}.{$assocData['foreignKey']}"))); + } + $query = array( + 'conditions' => $this->_mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $association, $assocData), $assocData['conditions']), + 'fields' => array_unique($assocData['fields']), + 'table' => $this->fullTableName($linkModel), + 'alias' => $association, + 'order' => $assocData['order'], + 'limit' => $assocData['limit'], + 'group' => null + ); + break; + case 'hasAndBelongsToMany': + $joinFields = array(); + $joinAssoc = null; + + if (isset($assocData['with']) && !empty($assocData['with'])) { + $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']); + list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys); + + $joinTbl = $this->fullTableName($model->{$with}); + $joinAlias = $joinTbl; + + if (is_array($joinFields) && !empty($joinFields)) { + $joinAssoc = $joinAlias = $model->{$with}->alias; + $joinFields = $this->fields($model->{$with}, $joinAlias, $joinFields); + } else { + $joinFields = array(); + } + } else { + $joinTbl = $this->fullTableName($assocData['joinTable']); + $joinAlias = $joinTbl; + } + $query = array( + 'conditions' => $assocData['conditions'], + 'limit' => $assocData['limit'], + 'table' => $this->fullTableName($linkModel), + 'alias' => $association, + 'fields' => array_merge($this->fields($linkModel, $association, $assocData['fields']), $joinFields), + 'order' => $assocData['order'], + 'group' => null, + 'joins' => array(array( + 'table' => $joinTbl, + 'alias' => $joinAssoc, + 'conditions' => $this->getConstraint('hasAndBelongsToMany', $model, $linkModel, $joinAlias, $assocData, $association) + )) + ); + break; + } + if (isset($query)) { + return $this->buildStatement($query, $model); + } + return null; + } + +/** + * Returns a conditions array for the constraint between two models + * + * @param string $type Association type + * @param Model $model Model object + * @param string $linkModel + * @param string $alias + * @param array $assoc + * @param string $alias2 + * @return array Conditions array defining the constraint between $model and $association + */ + public function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) { + $assoc += array('external' => false, 'self' => false); + + if (empty($assoc['foreignKey'])) { + return array(); + } + + switch (true) { + case ($assoc['external'] && $type === 'hasOne'): + return array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'); + case ($assoc['external'] && $type === 'belongsTo'): + return array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}'); + case (!$assoc['external'] && $type === 'hasOne'): + return array("{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}")); + case (!$assoc['external'] && $type === 'belongsTo'): + return array("{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}")); + case ($type === 'hasMany'): + return array("{$alias}.{$assoc['foreignKey']}" => array('{$__cakeID__$}')); + case ($type === 'hasAndBelongsToMany'): + return array( + array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'), + array("{$alias}.{$assoc['associationForeignKey']}" => $this->identifier("{$alias2}.{$linkModel->primaryKey}")) + ); + } + return array(); + } + +/** + * Builds and generates a JOIN statement from an array. Handles final clean-up before conversion. + * + * @param array $join An array defining a JOIN statement in a query + * @return string An SQL JOIN statement to be used in a query + * @see DboSource::renderJoinStatement() + * @see DboSource::buildStatement() + */ + public function buildJoinStatement($join) { + $data = array_merge(array( + 'type' => null, + 'alias' => null, + 'table' => 'join_table', + 'conditions' => array() + ), $join); + + if (!empty($data['alias'])) { + $data['alias'] = $this->alias . $this->name($data['alias']); + } + if (!empty($data['conditions'])) { + $data['conditions'] = trim($this->conditions($data['conditions'], true, false)); + } + return $this->renderJoinStatement($data); + } + +/** + * Builds and generates an SQL statement from an array. Handles final clean-up before conversion. + * + * @param array $query An array defining an SQL query + * @param Model $model The model object which initiated the query + * @return string An executable SQL statement + * @see DboSource::renderStatement() + */ + public function buildStatement($query, $model) { + $query = array_merge(array('offset' => null, 'joins' => array()), $query); + if (!empty($query['joins'])) { + $count = count($query['joins']); + for ($i = 0; $i < $count; $i++) { + if (is_array($query['joins'][$i])) { + $query['joins'][$i] = $this->buildJoinStatement($query['joins'][$i]); + } + } + } + return $this->renderStatement('select', array( + 'conditions' => $this->conditions($query['conditions'], true, true, $model), + 'fields' => implode(', ', $query['fields']), + 'table' => $query['table'], + 'alias' => $this->alias . $this->name($query['alias']), + 'order' => $this->order($query['order'], 'ASC', $model), + 'limit' => $this->limit($query['limit'], $query['offset']), + 'joins' => implode(' ', $query['joins']), + 'group' => $this->group($query['group'], $model) + )); + } + +/** + * Renders a final SQL JOIN statement + * + * @param array $data + * @return string + */ + public function renderJoinStatement($data) { + extract($data); + return trim("{$type} JOIN {$table} {$alias} ON ({$conditions})"); + } + +/** + * Renders a final SQL statement by putting together the component parts in the correct order + * + * @param string $type type of query being run. e.g select, create, update, delete, schema, alter. + * @param array $data Array of data to insert into the query. + * @return string Rendered SQL expression to be run. + */ + public function renderStatement($type, $data) { + extract($data); + $aliases = null; + + switch (strtolower($type)) { + case 'select': + return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}"; + case 'create': + return "INSERT INTO {$table} ({$fields}) VALUES ({$values})"; + case 'update': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} {$joins} "; + } + return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}"; + case 'delete': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} {$joins} "; + } + return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}"; + case 'schema': + foreach (array('columns', 'indexes', 'tableParameters') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } else { + ${$var} = ''; + } + } + if (trim($indexes) !== '') { + $columns .= ','; + } + return "CREATE TABLE {$table} (\n{$columns}{$indexes}){$tableParameters};"; + case 'alter': + return; + } + } + +/** + * Merges a mixed set of string/array conditions + * + * @param mixed $query + * @param mixed $assoc + * @return array + */ + protected function _mergeConditions($query, $assoc) { + if (empty($assoc)) { + return $query; + } + + if (is_array($query)) { + return array_merge((array)$assoc, $query); + } + + if (!empty($query)) { + $query = array($query); + if (is_array($assoc)) { + $query = array_merge($query, $assoc); + } else { + $query[] = $assoc; + } + return $query; + } + + return $assoc; + } + +/** + * Generates and executes an SQL UPDATE statement for given model, fields, and values. + * For databases that do not support aliases in UPDATE queries. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return boolean Success + */ + public function update(Model $model, $fields = array(), $values = null, $conditions = null) { + if ($values == null) { + $combined = $fields; + } else { + $combined = array_combine($fields, $values); + } + + $fields = implode(', ', $this->_prepareUpdateFields($model, $combined, empty($conditions))); + + $alias = $joins = null; + $table = $this->fullTableName($model); + $conditions = $this->_matchRecords($model, $conditions); + + if ($conditions === false) { + return false; + } + $query = compact('table', 'alias', 'joins', 'fields', 'conditions'); + + if (!$this->execute($this->renderStatement('update', $query))) { + $model->onError(); + return false; + } + return true; + } + +/** + * Quotes and prepares fields and values for an SQL UPDATE statement + * + * @param Model $model + * @param array $fields + * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets + * @param boolean $alias Include the model alias in the field name + * @return array Fields and values, quoted and preparted + */ + protected function _prepareUpdateFields($model, $fields, $quoteValues = true, $alias = false) { + $quotedAlias = $this->startQuote . $model->alias . $this->endQuote; + + $updates = array(); + foreach ($fields as $field => $value) { + if ($alias && strpos($field, '.') === false) { + $quoted = $model->escapeField($field); + } elseif (!$alias && strpos($field, '.') !== false) { + $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace( + $model->alias . '.', '', $field + ))); + } else { + $quoted = $this->name($field); + } + + if ($value === null) { + $updates[] = $quoted . ' = NULL'; + continue; + } + $update = $quoted . ' = '; + + if ($quoteValues) { + $update .= $this->value($value, $model->getColumnType($field)); + } elseif (!$alias) { + $update .= str_replace($quotedAlias . '.', '', str_replace( + $model->alias . '.', '', $value + )); + } else { + $update .= $value; + } + $updates[] = $update; + } + return $updates; + } + +/** + * Generates and executes an SQL DELETE statement. + * For databases that do not support aliases in UPDATE queries. + * + * @param Model $model + * @param mixed $conditions + * @return boolean Success + */ + public function delete(Model $model, $conditions = null) { + $alias = $joins = null; + $table = $this->fullTableName($model); + $conditions = $this->_matchRecords($model, $conditions); + + if ($conditions === false) { + return false; + } + + if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) { + $model->onError(); + return false; + } + return true; + } + +/** + * Gets a list of record IDs for the given conditions. Used for multi-record updates and deletes + * in databases that do not support aliases in UPDATE/DELETE queries. + * + * @param Model $model + * @param mixed $conditions + * @return array List of record IDs + */ + protected function _matchRecords($model, $conditions = null) { + if ($conditions === true) { + $conditions = $this->conditions(true); + } elseif ($conditions === null) { + $conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model); + } else { + $noJoin = true; + foreach ($conditions as $field => $value) { + $originalField = $field; + if (strpos($field, '.') !== false) { + list($alias, $field) = explode('.', $field); + $field = ltrim($field, $this->startQuote); + $field = rtrim($field, $this->endQuote); + } + if (!$model->hasField($field)) { + $noJoin = false; + break; + } + if ($field !== $originalField) { + $conditions[$field] = $value; + unset($conditions[$originalField]); + } + } + if ($noJoin === true) { + return $this->conditions($conditions); + } + $idList = $model->find('all', array( + 'fields' => "{$model->alias}.{$model->primaryKey}", + 'conditions' => $conditions + )); + + if (empty($idList)) { + return false; + } + $conditions = $this->conditions(array( + $model->primaryKey => Set::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}") + )); + } + return $conditions; + } + +/** + * Returns an array of SQL JOIN fragments from a model's associations + * + * @param Model $model + * @return array + */ + protected function _getJoins($model) { + $join = array(); + $joins = array_merge($model->getAssociated('hasOne'), $model->getAssociated('belongsTo')); + + foreach ($joins as $assoc) { + if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig && $model->{$assoc}->getDataSource()) { + $assocData = $model->getAssociated($assoc); + $join[] = $this->buildJoinStatement(array( + 'table' => $this->fullTableName($model->{$assoc}), + 'alias' => $assoc, + 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', + 'conditions' => trim($this->conditions( + $this->_mergeConditions($assocData['conditions'], $this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData)), + true, false, $model + )) + )); + } + } + return $join; + } + +/** + * Returns an SQL calculation, i.e. COUNT() or MAX() + * + * @param Model $model + * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' + * @param array $params Function parameters (any values must be quoted manually) + * @return string An SQL calculation function + */ + public function calculate($model, $func, $params = array()) { + $params = (array)$params; + + switch (strtolower($func)) { + case 'count': + if (!isset($params[0])) { + $params[0] = '*'; + } + if (!isset($params[1])) { + $params[1] = 'count'; + } + if (is_object($model) && $model->isVirtualField($params[0])){ + $arg = $this->_quoteFields($model->getVirtualField($params[0])); + } else { + $arg = $this->name($params[0]); + } + return 'COUNT(' . $arg . ') AS ' . $this->name($params[1]); + case 'max': + case 'min': + if (!isset($params[1])) { + $params[1] = $params[0]; + } + if (is_object($model) && $model->isVirtualField($params[0])) { + $arg = $this->_quoteFields($model->getVirtualField($params[0])); + } else { + $arg = $this->name($params[0]); + } + return strtoupper($func) . '(' . $arg . ') AS ' . $this->name($params[1]); + break; + } + } + +/** + * Deletes all the records in a table and resets the count of the auto-incrementing + * primary key, where applicable. + * + * @param mixed $table A string or model class representing the table to be truncated + * @return boolean SQL TRUNCATE TABLE statement, false if not applicable. + */ + public function truncate($table) { + return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table)); + } + +/** + * Begin a transaction + * + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + public function begin() { + if ($this->_transactionStarted || $this->_connection->beginTransaction()) { + $this->_transactionStarted = true; + $this->_transactionNesting++; + return true; + } + return false; + } + +/** + * Commit a transaction + * + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + public function commit() { + if ($this->_transactionStarted) { + $this->_transactionNesting--; + if ($this->_transactionNesting <= 0) { + $this->_transactionStarted = false; + $this->_transactionNesting = 0; + return $this->_connection->commit(); + } + return true; + } + return false; + } + +/** + * Rollback a transaction + * + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + public function rollback() { + if ($this->_transactionStarted && $this->_connection->rollBack()) { + $this->_transactionStarted = false; + $this->_transactionNesting = 0; + return true; + } + return false; + } + +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param mixed $source + * @return mixed + */ + public function lastInsertId($source = null) { + return $this->_connection->lastInsertId(); + } + +/** + * Creates a default set of conditions from the model if $conditions is null/empty. + * If conditions are supplied then they will be returned. If a model doesn't exist and no conditions + * were provided either null or false will be returned based on what was input. + * + * @param Model $model + * @param mixed $conditions Array of conditions, conditions string, null or false. If an array of conditions, + * or string conditions those conditions will be returned. With other values the model's existance will be checked. + * If the model doesn't exist a null or false will be returned depending on the input value. + * @param boolean $useAlias Use model aliases rather than table names when generating conditions + * @return mixed Either null, false, $conditions or an array of default conditions to use. + * @see DboSource::update() + * @see DboSource::conditions() + */ + public function defaultConditions($model, $conditions, $useAlias = true) { + if (!empty($conditions)) { + return $conditions; + } + $exists = $model->exists(); + if (!$exists && $conditions !== null) { + return false; + } elseif (!$exists) { + return null; + } + $alias = $model->alias; + + if (!$useAlias) { + $alias = $this->fullTableName($model, false); + } + return array("{$alias}.{$model->primaryKey}" => $model->getID()); + } + +/** + * Returns a key formatted like a string Model.fieldname(i.e. Post.title, or Country.name) + * + * @param Model $model + * @param string $key + * @param string $assoc + * @return string + */ + public function resolveKey(Model $model, $key, $assoc = null) { + if (empty($assoc)) { + $assoc = $model->alias; + } + if (strpos('.', $key) !== false) { + return $this->name($model->alias) . '.' . $this->name($key); + } + return $key; + } + +/** + * Private helper method to remove query metadata in given data array. + * + * @param array $data + * @return array + */ + protected function _scrubQueryData($data) { + static $base = null; + if ($base === null) { + $base = array_fill_keys(array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group'), array()); + } + return (array)$data + $base; + } + +/** + * Converts model virtual fields into sql expressions to be fetched later + * + * @param Model $model + * @param string $alias Alias tablename + * @param mixed $fields virtual fields to be used on query + * @return array + */ + protected function _constructVirtualFields($model, $alias, $fields) { + $virtual = array(); + foreach ($fields as $field) { + $virtualField = $this->name($alias . $this->virtualFieldSeparator . $field); + $expression = $this->_quoteFields($model->getVirtualField($field)); + $virtual[] = '(' . $expression . ") {$this->alias} {$virtualField}"; + } + return $virtual; + } + +/** + * Generates the fields list of an SQL query. + * + * @param Model $model + * @param string $alias Alias tablename + * @param mixed $fields + * @param boolean $quote If false, returns fields array unquoted + * @return array + */ + public function fields($model, $alias = null, $fields = array(), $quote = true) { + if (empty($alias)) { + $alias = $model->alias; + } + $virtualFields = $model->getVirtualField(); + $cacheKey = array( + $alias, + get_class($model), + $model->alias, + $virtualFields, + $fields, + $quote + ); + $cacheKey = md5(serialize($cacheKey)); + if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) { + return $return; + } + $allFields = empty($fields); + if ($allFields) { + $fields = array_keys($model->schema()); + } elseif (!is_array($fields)) { + $fields = String::tokenize($fields); + } + $fields = array_values(array_filter($fields)); + $allFields = $allFields || in_array('*', $fields) || in_array($model->alias . '.*', $fields); + + $virtual = array(); + if (!empty($virtualFields)) { + $virtualKeys = array_keys($virtualFields); + foreach ($virtualKeys as $field) { + $virtualKeys[] = $model->alias . '.' . $field; + } + $virtual = ($allFields) ? $virtualKeys : array_intersect($virtualKeys, $fields); + foreach ($virtual as $i => $field) { + if (strpos($field, '.') !== false) { + $virtual[$i] = str_replace($model->alias . '.', '', $field); + } + $fields = array_diff($fields, array($field)); + } + $fields = array_values($fields); + } + + if (!$quote) { + if (!empty($virtual)) { + $fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual)); + } + return $fields; + } + $count = count($fields); + + if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) { + for ($i = 0; $i < $count; $i++) { + if (is_string($fields[$i]) && in_array($fields[$i], $virtual)) { + unset($fields[$i]); + continue; + } + if (is_object($fields[$i]) && isset($fields[$i]->type) && $fields[$i]->type === 'expression') { + $fields[$i] = $fields[$i]->value; + } elseif (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){ + continue; + } elseif (!preg_match('/^.+\\(.*\\)/', $fields[$i])) { + $prepend = ''; + + if (strpos($fields[$i], 'DISTINCT') !== false) { + $prepend = 'DISTINCT '; + $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); + } + $dot = strpos($fields[$i], '.'); + + if ($dot === false) { + $prefix = !( + strpos($fields[$i], ' ') !== false || + strpos($fields[$i], '(') !== false + ); + $fields[$i] = $this->name(($prefix ? $alias . '.' : '') . $fields[$i]); + } else { + $value = array(); + if (strpos($fields[$i], ',') === false) { + $build = explode('.', $fields[$i]); + if (!Set::numeric($build)) { + $fields[$i] = $this->name(implode('.', $build)); + } + } + } + $fields[$i] = $prepend . $fields[$i]; + } elseif (preg_match('/\(([\.\w]+)\)/', $fields[$i], $field)) { + if (isset($field[1])) { + if (strpos($field[1], '.') === false) { + $field[1] = $this->name($alias . '.' . $field[1]); + } else { + $field[0] = explode('.', $field[1]); + if (!Set::numeric($field[0])) { + $field[0] = implode('.', array_map(array(&$this, 'name'), $field[0])); + $fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1); + } + } + } + } + } + } + if (!empty($virtual)) { + $fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual)); + } + return $this->cacheMethod(__FUNCTION__, $cacheKey, array_unique($fields)); + } + +/** + * Creates a WHERE clause by parsing given conditions data. If an array or string + * conditions are provided those conditions will be parsed and quoted. If a boolean + * is given it will be integer cast as condition. Null will return 1 = 1. + * + * Results of this method are stored in a memory cache. This improves performance, but + * because the method uses a simple hashing algorithm it can infrequently have collisions. + * Setting DboSource::$cacheMethods to false will disable the memory cache. + * + * @param mixed $conditions Array or string of conditions, or any value. + * @param boolean $quoteValues If true, values should be quoted + * @param boolean $where If true, "WHERE " will be prepended to the return value + * @param Model $model A reference to the Model instance making the query + * @return string SQL fragment + */ + public function conditions($conditions, $quoteValues = true, $where = true, $model = null) { + $clause = $out = ''; + + if ($where) { + $clause = ' WHERE '; + } + + if (is_array($conditions) && !empty($conditions)) { + $out = $this->conditionKeysToString($conditions, $quoteValues, $model); + + if (empty($out)) { + return $clause . ' 1 = 1'; + } + return $clause . implode(' AND ', $out); + } + if (is_bool($conditions)) { + return $clause . (int)$conditions . ' = 1'; + } + + if (empty($conditions) || trim($conditions) === '') { + return $clause . '1 = 1'; + } + $clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i'; + + if (preg_match($clauses, $conditions, $match)) { + $clause = ''; + } + $conditions = $this->_quoteFields($conditions); + return $clause . $conditions; + } + +/** + * Creates a WHERE clause by parsing given conditions array. Used by DboSource::conditions(). + * + * @param array $conditions Array or string of conditions + * @param boolean $quoteValues If true, values should be quoted + * @param Model $model A reference to the Model instance making the query + * @return string SQL fragment + */ + public function conditionKeysToString($conditions, $quoteValues = true, $model = null) { + $out = array(); + $data = $columnType = null; + $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); + + foreach ($conditions as $key => $value) { + $join = ' AND '; + $not = null; + + if (is_array($value)) { + $valueInsert = ( + !empty($value) && + (substr_count($key, '?') === count($value) || substr_count($key, ':') === count($value)) + ); + } + + if (is_numeric($key) && empty($value)) { + continue; + } elseif (is_numeric($key) && is_string($value)) { + $out[] = $not . $this->_quoteFields($value); + } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) { + if (in_array(strtolower(trim($key)), $bool)) { + $join = ' ' . strtoupper($key) . ' '; + } else { + $key = $join; + } + $value = $this->conditionKeysToString($value, $quoteValues, $model); + + if (strpos($join, 'NOT') !== false) { + if (strtoupper(trim($key)) === 'NOT') { + $key = 'AND ' . trim($key); + } + $not = 'NOT '; + } + + if (empty($value[1])) { + if ($not) { + $out[] = $not . '(' . $value[0] . ')'; + } else { + $out[] = $value[0] ; + } + } else { + $out[] = '(' . $not . '(' . implode(') ' . strtoupper($key) . ' (', $value) . '))'; + } + } else { + if (is_object($value) && isset($value->type)) { + if ($value->type === 'identifier') { + $data .= $this->name($key) . ' = ' . $this->name($value->value); + } elseif ($value->type === 'expression') { + if (is_numeric($key)) { + $data .= $value->value; + } else { + $data .= $this->name($key) . ' = ' . $value->value; + } + } + } elseif (is_array($value) && !empty($value) && !$valueInsert) { + $keys = array_keys($value); + if ($keys === array_values($keys)) { + $count = count($value); + if ($count === 1) { + $data = $this->_quoteFields($key) . ' = ('; + } else { + $data = $this->_quoteFields($key) . ' IN ('; + } + if ($quoteValues) { + if (is_object($model)) { + $columnType = $model->getColumnType($key); + } + $data .= implode(', ', $this->value($value, $columnType)); + } + $data .= ')'; + } else { + $ret = $this->conditionKeysToString($value, $quoteValues, $model); + if (count($ret) > 1) { + $data = '(' . implode(') AND (', $ret) . ')'; + } elseif (isset($ret[0])) { + $data = $ret[0]; + } + } + } elseif (is_numeric($key) && !empty($value)) { + $data = $this->_quoteFields($value); + } else { + $data = $this->_parseKey($model, trim($key), $value); + } + + if ($data != null) { + $out[] = $data; + $data = null; + } + } + } + return $out; + } + +/** + * Extracts a Model.field identifier and an SQL condition operator from a string, formats + * and inserts values, and composes them into an SQL snippet. + * + * @param Model $model Model object initiating the query + * @param string $key An SQL key snippet containing a field and optional SQL operator + * @param mixed $value The value(s) to be inserted in the string + * @return string + */ + protected function _parseKey($model, $key, $value) { + $operatorMatch = '/^((' . implode(')|(', $this->_sqlOps); + $operatorMatch .= '\\x20)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is'; + $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false)); + + if (strpos($key, ' ') === false) { + $operator = '='; + } else { + list($key, $operator) = explode(' ', trim($key), 2); + + if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) { + $key = $key . ' ' . $operator; + $split = strrpos($key, ' '); + $operator = substr($key, $split); + $key = substr($key, 0, $split); + } + } + + $virtual = false; + if (is_object($model) && $model->isVirtualField($key)) { + $key = $this->_quoteFields($model->getVirtualField($key)); + $virtual = true; + } + + $type = is_object($model) ? $model->getColumnType($key) : null; + $null = $value === null || (is_array($value) && empty($value)); + + if (strtolower($operator) === 'not') { + $data = $this->conditionKeysToString( + array($operator => array($key => $value)), true, $model + ); + return $data[0]; + } + + $value = $this->value($value, $type); + + if (!$virtual && $key !== '?') { + $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false); + $key = $isKey ? $this->_quoteFields($key) : $this->name($key); + } + + if ($bound) { + return String::insert($key . ' ' . trim($operator), $value); + } + + if (!preg_match($operatorMatch, trim($operator))) { + $operator .= ' ='; + } + $operator = trim($operator); + + if (is_array($value)) { + $value = implode(', ', $value); + + switch ($operator) { + case '=': + $operator = 'IN'; + break; + case '!=': + case '<>': + $operator = 'NOT IN'; + break; + } + $value = "({$value})"; + } elseif ($null) { + switch ($operator) { + case '=': + $operator = 'IS'; + break; + case '!=': + case '<>': + $operator = 'IS NOT'; + break; + } + } + if ($virtual) { + return "({$key}) {$operator} {$value}"; + } + return "{$key} {$operator} {$value}"; + } + +/** + * Quotes Model.fields + * + * @param string $conditions + * @return string or false if no match + */ + protected function _quoteFields($conditions) { + $start = $end = null; + $original = $conditions; + + if (!empty($this->startQuote)) { + $start = preg_quote($this->startQuote); + } + if (!empty($this->endQuote)) { + $end = preg_quote($this->endQuote); + } + $conditions = str_replace(array($start, $end), '', $conditions); + $conditions = preg_replace_callback('/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_' . $start . $end . ']*\\.[a-z0-9_' . $start . $end . ']*)/i', array(&$this, '_quoteMatchedField'), $conditions); + + if ($conditions !== null) { + return $conditions; + } + return $original; + } + +/** + * Auxiliary function to quote matches `Model.fields` from a preg_replace_callback call + * + * @param string $match matched string + * @return string quoted strig + */ + protected function _quoteMatchedField($match) { + if (is_numeric($match[0])) { + return $match[0]; + } + return $this->name($match[0]); + } + +/** + * Returns a limit statement in the correct format for the particular database. + * + * @param integer $limit Limit of results returned + * @param integer $offset Offset from which to start results + * @return string SQL limit/offset statement + */ + public function limit($limit, $offset = null) { + if ($limit) { + $rt = ''; + if (!strpos(strtolower($limit), 'limit')) { + $rt = ' LIMIT'; + } + + if ($offset) { + $rt .= ' ' . $offset . ','; + } + + $rt .= ' ' . $limit; + return $rt; + } + return null; + } + +/** + * Returns an ORDER BY clause as a string. + * + * @param array|string $keys Field reference, as a key (i.e. Post.title) + * @param string $direction Direction (ASC or DESC) + * @param Model $model model reference (used to look for virtual field) + * @return string ORDER BY clause + */ + public function order($keys, $direction = 'ASC', $model = null) { + if (!is_array($keys)) { + $keys = array($keys); + } + $keys = array_filter($keys); + $result = array(); + while (!empty($keys)) { + list($key, $dir) = each($keys); + array_shift($keys); + + if (is_numeric($key)) { + $key = $dir; + $dir = $direction; + } + + if (is_string($key) && strpos($key, ',') !== false && !preg_match('/\(.+\,.+\)/', $key)) { + $key = array_map('trim', explode(',', $key)); + } + if (is_array($key)) { + //Flatten the array + $key = array_reverse($key, true); + foreach ($key as $k => $v) { + if (is_numeric($k)) { + array_unshift($keys, $v); + } else { + $keys = array($k => $v) + $keys; + } + } + continue; + } elseif (is_object($key) && isset($key->type) && $key->type === 'expression') { + $result[] = $key->value; + continue; + } + + if (preg_match('/\\x20(ASC|DESC).*/i', $key, $_dir)) { + $dir = $_dir[0]; + $key = preg_replace('/\\x20(ASC|DESC).*/i', '', $key); + } + + $key = trim($key); + + if (is_object($model) && $model->isVirtualField($key)) { + $key = '(' . $this->_quoteFields($model->getVirtualField($key)) . ')'; + } + + if (strpos($key, '.')) { + $key = preg_replace_callback('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', array(&$this, '_quoteMatchedField'), $key); + } + if (!preg_match('/\s/', $key) && strpos($key, '.') === false) { + $key = $this->name($key); + } + $key .= ' ' . trim($dir); + $result[] = $key; + } + if (!empty($result)) { + return ' ORDER BY ' . implode(', ', $result); + } + return ''; + } + +/** + * Create a GROUP BY SQL clause + * + * @param string $group Group By Condition + * @param Model $model + * @return string string condition or null + */ + public function group($group, $model = null) { + if ($group) { + if (!is_array($group)) { + $group = array($group); + } + foreach($group as $index => $key) { + if (is_object($model) && $model->isVirtualField($key)) { + $group[$index] = '(' . $model->getVirtualField($key) . ')'; + } + } + $group = implode(', ', $group); + return ' GROUP BY ' . $this->_quoteFields($group); + } + return null; + } + +/** + * Disconnects database, kills the connection and says the connection is closed. + * + * @return void + */ + public function close() { + $this->disconnect(); + } + +/** + * Checks if the specified table contains any record matching specified SQL + * + * @param Model $Model Model to search + * @param string $sql SQL WHERE clause (condition only, not the "WHERE" part) + * @return boolean True if the table has a matching record, else false + */ + public function hasAny($Model, $sql) { + $sql = $this->conditions($sql); + $table = $this->fullTableName($Model); + $alias = $this->alias . $this->name($Model->alias); + $where = $sql ? "{$sql}" : ' WHERE 1 = 1'; + $id = $Model->escapeField(); + + $out = $this->fetchRow("SELECT COUNT({$id}) {$this->alias}count FROM {$table} {$alias}{$where}"); + + if (is_array($out)) { + return $out[0]['count']; + } + return false; + } + +/** + * Gets the length of a database-native column description, or null if no length + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + * @return mixed An integer or string representing the length of the column, or null for unknown length. + */ + public function length($real) { + if (!preg_match_all('/([\w\s]+)(?:\((\d+)(?:,(\d+))?\))?(\sunsigned)?(\szerofill)?/', $real, $result)) { + $col = str_replace(array(')', 'unsigned'), '', $real); + $limit = null; + + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } + if ($limit !== null) { + return intval($limit); + } + return null; + } + + $types = array( + 'int' => 1, 'tinyint' => 1, 'smallint' => 1, 'mediumint' => 1, 'integer' => 1, 'bigint' => 1 + ); + + list($real, $type, $length, $offset, $sign, $zerofill) = $result; + $typeArr = $type; + $type = $type[0]; + $length = $length[0]; + $offset = $offset[0]; + + $isFloat = in_array($type, array('dec', 'decimal', 'float', 'numeric', 'double')); + if ($isFloat && $offset) { + return $length . ',' . $offset; + } + + if (($real[0] == $type) && (count($real) === 1)) { + return null; + } + + if (isset($types[$type])) { + $length += $types[$type]; + if (!empty($sign)) { + $length--; + } + } elseif (in_array($type, array('enum', 'set'))) { + $length = 0; + foreach ($typeArr as $key => $enumValue) { + if ($key === 0) { + continue; + } + $tmpLength = strlen($enumValue); + if ($tmpLength > $length) { + $length = $tmpLength; + } + } + } + return intval($length); + } + +/** + * Translates between PHP boolean values and Database (faked) boolean values + * + * @param mixed $data Value to be translated + * @param boolean $quote + * @return string|boolean Converted boolean value + */ + public function boolean($data, $quote = false) { + if ($quote) { + return !empty($data) ? '1' : '0'; + } + return !empty($data); + } + +/** + * Inserts multiple values into a table + * + * @param string $table + * @param string $fields + * @param array $values + * @return boolean + */ + public function insertMulti($table, $fields, $values) { + $table = $this->fullTableName($table); + $holder = implode(',', array_fill(0, count($fields), '?')); + $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); + + $count = count($values); + $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$holder})"; + $statement = $this->_connection->prepare($sql); + $this->begin(); + for ($x = 0; $x < $count; $x++) { + $statement->execute($values[$x]); + $statement->closeCursor(); + } + return $this->commit(); + } + +/** + * Returns an array of the indexes in given datasource name. + * + * @param string $model Name of model to inspect + * @return array Fields in table. Keys are column and unique + */ + public function index($model) { + return false; + } + +/** + * Generate a database-native schema for the given Schema object + * + * @param Model $schema An instance of a subclass of CakeSchema + * @param string $tableName Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + public function createSchema($schema, $tableName = null) { + if (!is_a($schema, 'CakeSchema')) { + trigger_error(__d('cake_dev', 'Invalid schema object'), E_USER_WARNING); + return null; + } + $out = ''; + + foreach ($schema->tables as $curTable => $columns) { + if (!$tableName || $tableName == $curTable) { + $cols = $colList = $indexes = $tableParameters = array(); + $primary = null; + $table = $this->fullTableName($curTable); + + foreach ($columns as $name => $col) { + if (is_string($col)) { + $col = array('type' => $col); + } + if (isset($col['key']) && $col['key'] === 'primary') { + $primary = $name; + } + if ($name !== 'indexes' && $name !== 'tableParameters') { + $col['name'] = $name; + if (!isset($col['type'])) { + $col['type'] = 'string'; + } + $cols[] = $this->buildColumn($col); + } elseif ($name === 'indexes') { + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); + } elseif ($name === 'tableParameters') { + $tableParameters = array_merge($tableParameters, $this->buildTableParameters($col, $table)); + } + } + if (empty($indexes) && !empty($primary)) { + $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1)); + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); + } + $columns = $cols; + $out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes', 'tableParameters')) . "\n\n"; + } + } + return $out; + } + +/** + * Generate a alter syntax from CakeSchema::compare() + * + * @param mixed $compare + * @param string $table + * @return boolean + */ + public function alterSchema($compare, $table = null) { + return false; + } + +/** + * Generate a "drop table" statement for the given Schema object + * + * @param CakeSchema $schema An instance of a subclass of CakeSchema + * @param string $table Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + public function dropSchema(CakeSchema $schema, $table = null) { + $out = ''; + + foreach ($schema->tables as $curTable => $columns) { + if (!$table || $table == $curTable) { + $out .= 'DROP TABLE ' . $this->fullTableName($curTable) . ";\n"; + } + } + return $out; + } + +/** + * Generate a database-native column schema string + * + * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]), + * where options can be 'default', 'length', or 'key'. + * @return string + */ + public function buildColumn($column) { + $name = $type = null; + extract(array_merge(array('null' => true), $column)); + + if (empty($name) || empty($type)) { + trigger_error(__d('cake_dev', 'Column name or type not defined in schema'), E_USER_WARNING); + return null; + } + + if (!isset($this->columns[$type])) { + trigger_error(__d('cake_dev', 'Column type %s does not exist', $type), E_USER_WARNING); + return null; + } + + $real = $this->columns[$type]; + $out = $this->name($name) . ' ' . $real['name']; + + if (isset($column['length'])) { + $length = $column['length']; + } elseif (isset($column['limit'])) { + $length = $column['limit']; + } elseif (isset($real['length'])) { + $length = $real['length']; + } elseif (isset($real['limit'])) { + $length = $real['limit']; + } + if (isset($length)) { + $out .= '(' . $length . ')'; + } + + if (($column['type'] === 'integer' || $column['type'] === 'float' ) && isset($column['default']) && $column['default'] === '') { + $column['default'] = null; + } + $out = $this->_buildFieldParameters($out, $column, 'beforeDefault'); + + if (isset($column['key']) && $column['key'] === 'primary' && $type === 'integer') { + $out .= ' ' . $this->columns['primary_key']['name']; + } elseif (isset($column['key']) && $column['key'] === 'primary') { + $out .= ' NOT NULL'; + } elseif (isset($column['default']) && isset($column['null']) && $column['null'] === false) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL'; + } elseif (isset($column['default'])) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type); + } elseif ($type !== 'timestamp' && !empty($column['null'])) { + $out .= ' DEFAULT NULL'; + } elseif ($type === 'timestamp' && !empty($column['null'])) { + $out .= ' NULL'; + } elseif (isset($column['null']) && $column['null'] === false) { + $out .= ' NOT NULL'; + } + if ($type === 'timestamp' && isset($column['default']) && strtolower($column['default']) === 'current_timestamp') { + $out = str_replace(array("'CURRENT_TIMESTAMP'", "'current_timestamp'"), 'CURRENT_TIMESTAMP', $out); + } + return $this->_buildFieldParameters($out, $column, 'afterDefault'); + } + +/** + * Build the field parameters, in a position + * + * @param string $columnString The partially built column string + * @param array $columnData The array of column data. + * @param string $position The position type to use. 'beforeDefault' or 'afterDefault' are common + * @return string a built column with the field parameters added. + */ + protected function _buildFieldParameters($columnString, $columnData, $position) { + foreach ($this->fieldParameters as $paramName => $value) { + if (isset($columnData[$paramName]) && $value['position'] == $position) { + if (isset($value['options']) && !in_array($columnData[$paramName], $value['options'])) { + continue; + } + $val = $columnData[$paramName]; + if ($value['quote']) { + $val = $this->value($val); + } + $columnString .= ' ' . $value['value'] . $value['join'] . $val; + } + } + return $columnString; + } + +/** + * Format indexes for create table + * + * @param array $indexes + * @param string $table + * @return array + */ + public function buildIndex($indexes, $table = null) { + $join = array(); + foreach ($indexes as $name => $value) { + $out = ''; + if ($name === 'PRIMARY') { + $out .= 'PRIMARY '; + $name = null; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + $name = $this->startQuote . $name . $this->endQuote; + } + if (is_array($value['column'])) { + $out .= 'KEY ' . $name . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; + } else { + $out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')'; + } + $join[] = $out; + } + return $join; + } + +/** + * Read additional table parameters + * + * @param string $name + * @return array + */ + public function readTableParameters($name) { + $parameters = array(); + if (method_exists($this, 'listDetailedSources')) { + $currentTableDetails = $this->listDetailedSources($name); + foreach ($this->tableParameters as $paramName => $parameter) { + if (!empty($parameter['column']) && !empty($currentTableDetails[$parameter['column']])) { + $parameters[$paramName] = $currentTableDetails[$parameter['column']]; + } + } + } + return $parameters; + } + +/** + * Format parameters for create table + * + * @param array $parameters + * @param string $table + * @return array + */ + public function buildTableParameters($parameters, $table = null) { + $result = array(); + foreach ($parameters as $name => $value) { + if (isset($this->tableParameters[$name])) { + if ($this->tableParameters[$name]['quote']) { + $value = $this->value($value); + } + $result[] = $this->tableParameters[$name]['value'] . $this->tableParameters[$name]['join'] . $value; + } + } + return $result; + } + +/** + * Guesses the data type of an array + * + * @param string $value + * @return void + */ + public function introspectType($value) { + if (!is_array($value)) { + if (is_bool($value)) { + return 'boolean'; + } + if (is_float($value) && floatval($value) === $value) { + return 'float'; + } + if (is_int($value) && intval($value) === $value) { + return 'integer'; + } + if (is_string($value) && strlen($value) > 255) { + return 'text'; + } + return 'string'; + } + + $isAllFloat = $isAllInt = true; + $containsFloat = $containsInt = $containsString = false; + foreach ($value as $key => $valElement) { + $valElement = trim($valElement); + if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) { + $isAllFloat = false; + } else { + $containsFloat = true; + continue; + } + if (!is_int($valElement) && !preg_match('/^[\d]+$/', $valElement)) { + $isAllInt = false; + } else { + $containsInt = true; + continue; + } + $containsString = true; + } + + if ($isAllFloat) { + return 'float'; + } + if ($isAllInt) { + return 'integer'; + } + + if ($containsInt && !$containsString) { + return 'integer'; + } + return 'string'; + } + +/** + * Writes a new key for the in memory sql query cache + * + * @param string $sql SQL query + * @param mixed $data result of $sql query + * @param array $params query params bound as values + * @return void + */ + protected function _writeQueryCache($sql, $data, $params = array()) { + if (preg_match('/^\s*select/i', $sql)) { + $this->_queryCache[$sql][serialize($params)] = $data; + } + } + +/** + * Returns the result for a sql query if it is already cached + * + * @param string $sql SQL query + * @param array $params query params bound as values + * @return mixed results for query if it is cached, false otherwise + */ + public function getQueryCache($sql, $params = array()) { + if (isset($this->_queryCache[$sql]) && preg_match('/^\s*select/i', $sql)) { + $serialized = serialize($params); + if (isset($this->_queryCache[$sql][$serialized])) { + return $this->_queryCache[$sql][$serialized]; + } + } + return false; + } + +/** + * Used for storing in cache the results of the in-memory methodCache + * + */ + public function __destruct() { + if ($this->_methodCacheChange) { + Cache::write('method_cache', self::$methodCache, '_cake_core_'); + } + } + +} diff --git a/app/Cake/Model/Datasource/Session/CacheSession.php b/app/Cake/Model/Datasource/Session/CacheSession.php new file mode 100644 index 00000000..6ef8b2a4 --- /dev/null +++ b/app/Cake/Model/Datasource/Session/CacheSession.php @@ -0,0 +1,91 @@ +'Session', + 'alias' => 'Session', + 'table' => 'cake_sessions', + ); + } else { + $settings = array( + 'class' =>$modelName, + 'alias' => 'Session', + ); + } + ClassRegistry::init($settings); + } + +/** + * Method called on open of a database session. + * + * @return boolean Success + */ + public function open() { + return true; + } + +/** + * Method called on close of a database session. + * + * @return boolean Success + */ + public function close() { + $probability = mt_rand(1, 150); + if ($probability <= 3) { + $this->gc(); + } + return true; + } + +/** + * Method used to read from a database session. + * + * @param mixed $id The key of the value to read + * @return mixed The value of the key or false if it does not exist + */ + public function read($id) { + $model = ClassRegistry::getObject('Session'); + + $row = $model->find('first', array( + 'conditions' => array($model->primaryKey => $id) + )); + + if (empty($row[$model->alias]['data'])) { + return false; + } + + return $row[$model->alias]['data']; + } + +/** + * Helper function called on write for database sessions. + * + * @param integer $id ID that uniquely identifies session in database + * @param mixed $data The value of the data to be saved. + * @return boolean True for successful write, false otherwise. + */ + public function write($id, $data) { + if (!$id) { + return false; + } + $expires = time() + (Configure::read('Session.timeout') * 60); + $Session = ClassRegistry::getObject('Session'); + $record = compact('id', 'data', 'expires'); + $record[$Session->primaryKey] = $id; + return $Session->save($record); + } + +/** + * Method called on the destruction of a database session. + * + * @param integer $id ID that uniquely identifies session in database + * @return boolean True for successful delete, false otherwise. + */ + public function destroy($id) { + return ClassRegistry::getObject('Session')->delete($id); + } + +/** + * Helper function called on gc for database sessions. + * + * @param integer $expires Timestamp (defaults to current time) + * @return boolean Success + */ + public function gc($expires = null) { + if (!$expires) { + $expires = time(); + } + $model = ClassRegistry::getObject('Session'); + return $model->deleteAll(array($model->alias . ".expires <" => $expires), false, false); + } +} diff --git a/app/Cake/Model/Model.php b/app/Cake/Model/Model.php new file mode 100644 index 00000000..63e8f6c8 --- /dev/null +++ b/app/Cake/Model/Model.php @@ -0,0 +1,3429 @@ + table 'users'; class 'Man' => table 'men') + * The table is required to have at least 'id auto_increment' primary key. + * + * @package Cake.Model + * @link http://book.cakephp.org/view/1000/Models + */ +class Model extends Object { + +/** + * The name of the DataSource connection that this Model uses + * + * The value must be an attribute name that you defined in `app/Config/database.php` + * or created using `ConnectionManager::create()`. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#useDbConfig-1058 + */ + public $useDbConfig = 'default'; + +/** + * Custom database table name, or null/false if no table association is desired. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#useTable-1059 + */ + public $useTable = null; + +/** + * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements. + * + * This field is also used in `find('list')` when called with no extra parameters in the fields list + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#displayField-1062 + */ + public $displayField = null; + +/** + * Value of the primary key ID of the record that this model is currently pointing to. + * Automatically set after database insertions. + * + * @var mixed + */ + public $id = false; + +/** + * Container for the data that this model gets from persistent storage (usually, a database). + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#data-1065 + */ + public $data = array(); + +/** + * Table name for this Model. + * + * @var string + */ + public $table = false; + +/** + * The name of the primary key field for this model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#primaryKey-1061 + */ + public $primaryKey = null; + +/** + * Field-by-field table metadata. + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#_schema-1066 + */ + protected $_schema = null; + +/** + * List of validation rules. It must be an array with the field name as key and using + * as value one of the following possibilities + * + * ### Validating using regular expressions + * + * {{{ + * public $validate = array( + * 'name' => '/^[a-z].+$/i' + * ); + * }}} + * + * ### Validating using methods (no parameters) + * + * {{{ + * public $validate = array( + * 'name' => 'notEmpty' + * ); + * }}} + * + * ### Validating using methods (with parameters) + * + * {{{ + * public $validate = array( + * 'age' => array( + * 'rule' => array('between', 5, 25) + * ) + * ); + * }}} + * + * ### Validating using custom method + * + * {{{ + * public $validate = array( + * 'password' => array( + * 'rule' => array('customValidation') + * ) + * ); + * public function customValidation($data) { + * // $data will contain array('password' => 'value') + * if (isset($this->data[$this->alias]['password2'])) { + * return $this->data[$this->alias]['password2'] === current($data); + * } + * return true; + * } + * }}} + * + * ### Validations with messages + * + * The messages will be used in Model::$validationErrors and can be used in the FormHelper + * + * {{{ + * public $validate = array( + * 'age' => array( + * 'rule' => array('between', 5, 25), + * 'message' => array('The age must be between %d and %d.') + * ) + * ); + * }}} + * + * ### Multiple validations to the same field + * + * {{{ + * public $validate = array( + * 'login' => array( + * array( + * 'role' => 'alphaNumeric', + * 'message' => 'Only alphabets and numbers allowed', + * 'last' => true + * ), + * array( + * 'role' => array('minLength', 8), + * 'message' => array('Minimum length of %d characters') + * ) + * ) + * ); + * }}} + * + * ### Valid keys in validations + * + * - `role`: String with method name, regular expression (started by slash) or array with method and parameters + * - `message`: String with the message or array if have multiple parameters. See http://php.net/sprintf + * - `last`: Boolean value to indicate if continue validating the others rules if the current fail [Default: true] + * - `required`: Boolean value to indicate if the field must be present on save + * - `allowEmpty`: Boolean value to indicate if the field can be empty + * - `on`: Possible values: `update`, `create`. Indicate to apply this rule only on update or create + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#validate-1067 + * @link http://book.cakephp.org/view/1143/Data-Validation + */ + public $validate = array(); + +/** + * List of validation errors. + * + * @var array + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public $validationErrors = array(); + + +/** + * Name of the validation string domain to use when translating validation errors. + * + * @var string + */ + public $validationDomain = null; + +/** + * Database table prefix for tables in model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#tablePrefix-1060 + */ + public $tablePrefix = null; + +/** + * Name of the model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#name-1068 + */ + public $name = null; + +/** + * Alias name for model. + * + * @var string + */ + public $alias = null; + +/** + * List of table names included in the model description. Used for associations. + * + * @var array + */ + public $tableToModel = array(); + +/** + * Whether or not to cache queries for this model. This enables in-memory + * caching only, the results are not stored beyond the current request. + * + * @var boolean + * @link http://book.cakephp.org/view/1057/Model-Attributes#cacheQueries-1069 + */ + public $cacheQueries = false; + +/** + * Detailed list of belongsTo associations. + * + * ### Basic usage + * + * `public $belongsTo = array('Group', 'Department');` + * + * ### Detailed configuration + * + * {{{ + * public $belongsTo = array( + * 'Group', + * 'Department' => array( + * 'className' => 'Department', + * 'foreignKey' => 'department_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘Profile belongsTo User’ relationship, the className key should equal ‘User.’ + * - `foreignKey`: the name of the foreign key found in the current model. This is + * especially handy if you need to define multiple belongsTo relationships. The default + * value for this key is the underscored, singular name of the other model, suffixed with ‘_id’. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “User.active = 1†is always + * better than just “active = 1.†+ * - `type`: the type of the join to use in the SQL query, default is LEFT which + * may not fit your needs in all situations, INNER may be helpful when you want + * everything from your main and associated models or nothing at all!(effective + * when used with some conditions of course). (NB: type value is in lower case - i.e. left, inner) + * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `counterCache`: If set to true the associated Model will automatically increase or + * decrease the “[singular_model_name]_count†field in the foreign table whenever you do + * a save() or delete(). If its a string then its the field name to use. The value in the + * counter field represents the number of related rows. + * - `counterScope`: Optional conditions array to use for updating counter cache field. + * + * @var array + * @link http://book.cakephp.org/view/1042/belongsTo + */ + public $belongsTo = array(); + +/** + * Detailed list of hasOne associations. + * + * ### Basic usage + * + * `public $hasOne = array('Profile', 'Address');` + * + * ### Detailed configuration + * + * {{{ + * public $hasOne = array( + * 'Profile', + * 'Address' => array( + * 'className' => 'Address', + * 'foreignKey' => 'user_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘User hasOne Profile’ relationship, the className key should equal ‘Profile.’ + * - `foreignKey`: the name of the foreign key found in the other model. This is + * especially handy if you need to define multiple hasOne relationships. + * The default value for this key is the underscored, singular name of the + * current model, suffixed with ‘_id’. In the example above it would default to 'user_id'. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “Profile.approved = 1†is + * always better than just “approved = 1.†+ * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `dependent`: When the dependent key is set to true, and the model’s delete() + * method is called with the cascade parameter set to true, associated model + * records are also deleted. In this case we set it true so that deleting a + * User will also delete her associated Profile. + * + * @var array + * @link http://book.cakephp.org/view/1041/hasOne + */ + public $hasOne = array(); + +/** + * Detailed list of hasMany associations. + * + * ### Basic usage + * + * `public $hasMany = array('Comment', 'Task');` + * + * ### Detailed configuration + * + * {{{ + * public $hasMany = array( + * 'Comment', + * 'Task' => array( + * 'className' => 'Task', + * 'foreignKey' => 'user_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘User hasMany Comment’ relationship, the className key should equal ‘Comment.’ + * - `foreignKey`: the name of the foreign key found in the other model. This is + * especially handy if you need to define multiple hasMany relationships. The default + * value for this key is the underscored, singular name of the actual model, suffixed with ‘_id’. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “Comment.status = 1†is always + * better than just “status = 1.†+ * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `limit`: The maximum number of associated rows you want returned. + * - `offset`: The number of associated rows to skip over (given the current + * conditions and order) before fetching and associating. + * - `dependent`: When dependent is set to true, recursive model deletion is + * possible. In this example, Comment records will be deleted when their + * associated User record has been deleted. + * - `exclusive`: When exclusive is set to true, recursive model deletion does + * the delete with a deleteAll() call, instead of deleting each entity separately. + * This greatly improves performance, but may not be ideal for all circumstances. + * - `finderQuery`: A complete SQL query CakePHP can use to fetch associated model + * records. This should be used in situations that require very custom results. + * + * @var array + * @link http://book.cakephp.org/view/1043/hasMany + */ + public $hasMany = array(); + +/** + * Detailed list of hasAndBelongsToMany associations. + * + * ### Basic usage + * + * `public $hasAndBelongsToMany = array('Role', 'Address');` + * + * ### Detailed configuration + * + * {{{ + * public $hasAndBelongsToMany = array( + * 'Role', + * 'Address' => array( + * 'className' => 'Address', + * 'foreignKey' => 'user_id', + * 'associationForeignKey' => 'address_id', + * 'joinTable' => 'addresses_users' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you're defining a ‘Recipe HABTM Tag' relationship, the className key should equal ‘Tag.' + * - `joinTable`: The name of the join table used in this association (if the + * current table doesn't adhere to the naming convention for HABTM join tables). + * - `with`: Defines the name of the model for the join table. By default CakePHP + * will auto-create a model for you. Using the example above it would be called + * RecipesTag. By using this key you can override this default name. The join + * table model can be used just like any "regular" model to access the join table directly. + * - `foreignKey`: the name of the foreign key found in the current model. + * This is especially handy if you need to define multiple HABTM relationships. + * The default value for this key is the underscored, singular name of the + * current model, suffixed with ‘_id'. + * - `associationForeignKey`: the name of the foreign key found in the other model. + * This is especially handy if you need to define multiple HABTM relationships. + * The default value for this key is the underscored, singular name of the other + * model, suffixed with ‘_id'. + * - `unique`: If true (default value) cake will first delete existing relationship + * records in the foreign keys table before inserting new ones, when updating a + * record. So existing associations need to be passed again when updating. + * - `conditions`: An SQL fragment used to filter related model records. It's good + * practice to use model names in SQL fragments: "Comment.status = 1" is always + * better than just "status = 1." + * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `limit`: The maximum number of associated rows you want returned. + * - `offset`: The number of associated rows to skip over (given the current + * conditions and order) before fetching and associating. + * - `finderQuery`, `deleteQuery`, `insertQuery`: A complete SQL query CakePHP + * can use to fetch, delete, or create new associated model records. This should + * be used in situations that require very custom results. + * + * @var array + * @link http://book.cakephp.org/view/1044/hasAndBelongsToMany-HABTM + */ + public $hasAndBelongsToMany = array(); + +/** + * List of behaviors to load when the model object is initialized. Settings can be + * passed to behaviors by using the behavior name as index. Eg: + * + * public $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1')) + * + * @var array + * @link http://book.cakephp.org/view/1072/Using-Behaviors + */ + public $actsAs = null; + +/** + * Holds the Behavior objects currently bound to this model. + * + * @var BehaviorCollection + */ + public $Behaviors = null; + +/** + * Whitelist of fields allowed to be saved. + * + * @var array + */ + public $whitelist = array(); + +/** + * Whether or not to cache sources for this model. + * + * @var boolean + */ + public $cacheSources = true; + +/** + * Type of find query currently executing. + * + * @var string + */ + public $findQueryType = null; + +/** + * Number of associations to recurse through during find calls. Fetches only + * the first level by default. + * + * @var integer + * @link http://book.cakephp.org/view/1057/Model-Attributes#recursive-1063 + */ + public $recursive = 1; + +/** + * The column name(s) and direction(s) to order find results by default. + * + * public $order = "Post.created DESC"; + * public $order = array("Post.view_count DESC", "Post.rating DESC"); + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#order-1064 + */ + public $order = null; + +/** + * Array of virtual fields this model has. Virtual fields are aliased + * SQL expressions. Fields added to this property will be read as other fields in a model + * but will not be saveable. + * + * `public $virtualFields = array('two' => '1 + 1');` + * + * Is a simplistic example of how to set virtualFields + * + * @var array + */ + public $virtualFields = array(); + +/** + * Default list of association keys. + * + * @var array + */ + protected $_associationKeys = array( + 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'), + 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'), + 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'), + 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery') + ); + +/** + * Holds provided/generated association key names and other data for all associations. + * + * @var array + */ + protected $_associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + +/** + * Holds model associations temporarily to allow for dynamic (un)binding. + * + * @var array + */ + public $__backAssociation = array(); + +/** + * Back inner association + * + * @var array + */ + public $__backInnerAssociation = array(); + +/** + * Back original association + * + * @var array + */ + public $__backOriginalAssociation = array(); + +/** + * Back containable association + * + * @var array + */ + public $__backContainableAssociation = array(); + +/** + * The ID of the model record that was last inserted. + * + * @var integer + */ + protected $_insertID = null; + +/** + * Has the datasource been configured. + * + * @var boolean + * @see Model::getDataSource + */ + protected $_sourceConfigured = false; + +/** + * List of valid finder method options, supplied as the first parameter to find(). + * + * @var array + */ + public $findMethods = array( + 'all' => true, 'first' => true, 'count' => true, + 'neighbors' => true, 'list' => true, 'threaded' => true + ); + +/** + * Constructor. Binds the model's database table to the object. + * + * If `$id` is an array it can be used to pass several options into the model. + * + * - id - The id to start the model on. + * - table - The table to use for this model. + * - ds - The connection name this model is connected to. + * - name - The name of the model eg. Post. + * - alias - The alias of the model, this is used for registering the instance in the `ClassRegistry`. + * eg. `ParentThread` + * + * ### Overriding Model's __construct method. + * + * When overriding Model::__construct() be careful to include and pass in all 3 of the + * arguments to `parent::__construct($id, $table, $ds);` + * + * ### Dynamically creating models + * + * You can dynamically create model instances using the $id array syntax. + * + * {{{ + * $Post = new Model(array('table' => 'posts', 'name' => 'Post', 'ds' => 'connection2')); + * }}} + * + * Would create a model attached to the posts table on connection2. Dynamic model creation is useful + * when you want a model object that contains no associations or attached behaviors. + * + * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above. + * @param string $table Name of database table to use. + * @param string $ds DataSource connection name. + */ + public function __construct($id = false, $table = null, $ds = null) { + parent::__construct(); + + if (is_array($id)) { + extract(array_merge( + array( + 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig, + 'name' => $this->name, 'alias' => $this->alias + ), + $id + )); + } + + if ($this->name === null) { + $this->name = (isset($name) ? $name : get_class($this)); + } + + if ($this->alias === null) { + $this->alias = (isset($alias) ? $alias : $this->name); + } + + if ($this->primaryKey === null) { + $this->primaryKey = 'id'; + } + + ClassRegistry::addObject($this->alias, $this); + + $this->id = $id; + unset($id); + + if ($table === false) { + $this->useTable = false; + } elseif ($table) { + $this->useTable = $table; + } + + if ($ds !== null) { + $this->useDbConfig = $ds; + } + + if (is_subclass_of($this, 'AppModel')) { + $merge = array('findMethods'); + if ($this->actsAs !== null || $this->actsAs !== false) { + $merge[] = 'actsAs'; + } + $parentClass = get_parent_class($this); + if ($parentClass !== 'AppModel') { + $this->_mergeVars($merge, $parentClass); + } + $this->_mergeVars($merge, 'AppModel'); + } + $this->Behaviors = new BehaviorCollection(); + + if ($this->useTable !== false) { + + if ($this->useTable === null) { + $this->useTable = Inflector::tableize($this->name); + } + + if ($this->displayField == null) { + unset($this->displayField); + } + $this->table = $this->useTable; + $this->tableToModel[$this->table] = $this->alias; + } elseif ($this->table === false) { + $this->table = Inflector::tableize($this->name); + } + $this->_createLinks(); + $this->Behaviors->init($this->alias, $this->actsAs); + } + +/** + * Handles custom method calls, like findBy for DB models, + * and custom RPC calls for remote data sources. + * + * @param string $method Name of method to call. + * @param array $params Parameters for the method. + * @return mixed Whatever is returned by called method + */ + public function __call($method, $params) { + $result = $this->Behaviors->dispatchMethod($this, $method, $params); + if ($result !== array('unhandled')) { + return $result; + } + $return = $this->getDataSource()->query($method, $params, $this); + return $return; + } + +/** + * Handles the lazy loading of model associations by lookin in the association arrays for the requested variable + * + * @param string $name variable tested for existance in class + * @return boolean true if the variable exists (if is a not loaded model association it will be created), false otherwise + */ + public function __isset($name) { + $className = false; + + foreach ($this->_associations as $type) { + if (isset($name, $this->{$type}[$name])) { + $className = empty($this->{$type}[$name]['className']) ? $name : $this->{$type}[$name]['className']; + break; + } else if ($type == 'hasAndBelongsToMany') { + foreach ($this->{$type} as $k => $relation) { + if (empty($relation['with'])) { + continue; + } + if (is_array($relation['with'])) { + if (key($relation['with']) === $name) { + $className = $name; + } + } else { + list($plugin, $class) = pluginSplit($relation['with']); + if ($class === $name) { + $className = $relation['with']; + } + } + if ($className) { + $assocKey = $k; + $dynamic = !empty($relation['dynamicWith']); + break(2); + } + } + } + } + + if (!$className) { + return false; + } + + list($plugin, $className) = pluginSplit($className); + + if (!ClassRegistry::isKeySet($className) && !empty($dynamic)) { + $this->{$className} = new AppModel(array( + 'name' => $className, + 'table' => $this->hasAndBelongsToMany[$assocKey]['joinTable'], + 'ds' => $this->useDbConfig + )); + } else { + $this->_constructLinkedModel($name, $className, $plugin); + } + + if (!empty($assocKey)) { + $this->hasAndBelongsToMany[$assocKey]['joinTable'] = $this->{$name}->table; + if (count($this->{$name}->schema()) <= 2 && $this->{$name}->primaryKey !== false) { + $this->{$name}->primaryKey = $this->hasAndBelongsToMany[$assocKey]['foreignKey']; + } + } + + return true; + } + +/** + * Returns the value of the requested variable if it can be set by __isset() + * + * @param string $name variable requested for it's value or reference + * @return mixed value of requested variable if it is set + */ + public function __get($name) { + if ($name === 'displayField') { + return $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey)); + } + if (isset($this->{$name})) { + return $this->{$name}; + } + } + +/** + * Bind model associations on the fly. + * + * If `$reset` is false, association will not be reset + * to the originals defined in the model + * + * Example: Add a new hasOne binding to the Profile model not + * defined in the model source code: + * + * `$this->User->bindModel( array('hasOne' => array('Profile')) );` + * + * Bindings that are not made permanent will be reset by the next Model::find() call on this + * model. + * + * @param array $params Set of bindings (indexed by binding type) + * @param boolean $reset Set to false to make the binding permanent + * @return boolean Success + * @link http://book.cakephp.org/view/1045/Creating-and-Destroying-Associations-on-the-Fly + */ + public function bindModel($params, $reset = true) { + foreach ($params as $assoc => $model) { + if ($reset === true && !isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + foreach ($model as $key => $value) { + $assocName = $key; + + if (is_numeric($key)) { + $assocName = $value; + $value = array(); + } + $modelName = $assocName; + $this->{$assoc}[$assocName] = $value; + if (property_exists($this, $assocName)) { + unset($this->{$assocName}); + } + if ($reset === false && isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc][$assocName] = $value; + } + } + } + $this->_createLinks(); + return true; + } + +/** + * Turn off associations on the fly. + * + * If $reset is false, association will not be reset + * to the originals defined in the model + * + * Example: Turn off the associated Model Support request, + * to temporarily lighten the User model: + * + * `$this->User->unbindModel( array('hasMany' => array('Supportrequest')) );` + * + * unbound models that are not made permanent will reset with the next call to Model::find() + * + * @param array $params Set of bindings to unbind (indexed by binding type) + * @param boolean $reset Set to false to make the unbinding permanent + * @return boolean Success + * @link http://book.cakephp.org/view/1045/Creating-and-Destroying-Associations-on-the-Fly + */ + public function unbindModel($params, $reset = true) { + foreach ($params as $assoc => $models) { + if ($reset === true && !isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + foreach ($models as $model) { + if ($reset === false && isset($this->__backAssociation[$assoc][$model])) { + unset($this->__backAssociation[$assoc][$model]); + } + unset($this->{$assoc}[$model]); + } + } + return true; + } + +/** + * Create a set of associations. + * + * @return void + */ + protected function _createLinks() { + foreach ($this->_associations as $type) { + if (!is_array($this->{$type})) { + $this->{$type} = explode(',', $this->{$type}); + + foreach ($this->{$type} as $i => $className) { + $className = trim($className); + unset ($this->{$type}[$i]); + $this->{$type}[$className] = array(); + } + } + + if (!empty($this->{$type})) { + foreach ($this->{$type} as $assoc => $value) { + $plugin = null; + + if (is_numeric($assoc)) { + unset ($this->{$type}[$assoc]); + $assoc = $value; + $value = array(); + $this->{$type}[$assoc] = $value; + + if (strpos($assoc, '.') !== false) { + list($plugin, $assoc) = pluginSplit($assoc); + $this->{$type}[$assoc] = array('className' => $plugin. '.' . $assoc); + } + } + $this->_generateAssociation($type, $assoc); + } + } + } + } + +/** + * Protected helper method to create associated models of a given class. + * + * @param string $assoc Association name + * @param string $className Class name + * @param string $plugin name of the plugin where $className is located + * examples: public $hasMany = array('Assoc' => array('className' => 'ModelName')); + * usage: $this->Assoc->modelMethods(); + * + * public $hasMany = array('ModelName'); + * usage: $this->ModelName->modelMethods(); + * @return void + */ + protected function _constructLinkedModel($assoc, $className = null, $plugin = null) { + if (empty($className)) { + $className = $assoc; + } + + if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) { + if ($plugin) { + $plugin .= '.'; + } + $model = array('class' => $plugin . $className, 'alias' => $assoc); + $this->{$assoc} = ClassRegistry::init($model); + if ($plugin) { + ClassRegistry::addObject($plugin . $className, $this->{$assoc}); + } + if ($assoc) { + $this->tableToModel[$this->{$assoc}->table] = $assoc; + } + } + } + +/** + * Build an array-based association from string. + * + * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' + * @param string $assocKey + * @return void + */ + protected function _generateAssociation($type, $assocKey) { + $class = $assocKey; + $dynamicWith = false; + + foreach ($this->_associationKeys[$type] as $key) { + + if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) { + $data = ''; + + switch ($key) { + case 'fields': + $data = ''; + break; + + case 'foreignKey': + $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id'; + break; + + case 'associationForeignKey': + $data = Inflector::singularize($this->{$class}->table) . '_id'; + break; + + case 'with': + $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable'])); + $dynamicWith = true; + break; + + case 'joinTable': + $tables = array($this->table, $this->{$class}->table); + sort ($tables); + $data = $tables[0] . '_' . $tables[1]; + break; + + case 'className': + $data = $class; + break; + + case 'unique': + $data = true; + break; + } + $this->{$type}[$assocKey][$key] = $data; + } + + if ($dynamicWith) { + $this->{$type}[$assocKey]['dynamicWith'] = true; + } + + } + } + +/** + * Sets a custom table for your controller class. Used by your controller to select a database table. + * + * @param string $tableName Name of the custom table + * @throws MissingTableException when database table $tableName is not found on data source + * @return void + */ + public function setSource($tableName) { + $this->setDataSource($this->useDbConfig); + $db = ConnectionManager::getDataSource($this->useDbConfig); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + + if (method_exists($db, 'listSources')) { + $sources = $db->listSources(); + if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) { + throw new MissingTableException(array( + 'table' => $this->tablePrefix . $tableName, + 'class' => $this->alias + )); + } + $this->_schema = null; + } + $this->table = $this->useTable = $tableName; + $this->tableToModel[$this->table] = $this->alias; + } + +/** + * This function does two things: + * + * 1. it scans the array $one for the primary key, + * and if that's found, it sets the current id to the value of $one[id]. + * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object. + * 2. Returns an array with all of $one's keys and values. + * (Alternative indata: two strings, which are mangled to + * a one-item, two-dimensional array using $one for a key and $two as its value.) + * + * @param mixed $one Array or string of data + * @param string $two Value string for the alternative indata method + * @return array Data with all of $one's keys and values + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function set($one, $two = null) { + if (!$one) { + return; + } + if (is_object($one)) { + if ($one instanceof SimpleXMLElement || $one instanceof DOMNode) { + $one = $this->_normalizeXmlData(Xml::toArray($one)); + } else { + $one = Set::reverse($one); + } + } + + if (is_array($one)) { + $data = $one; + if (empty($one[$this->alias])) { + if ($this->getAssociated(key($one)) === null) { + $data = array($this->alias => $one); + } + } + } else { + $data = array($this->alias => array($one => $two)); + } + + foreach ($data as $modelName => $fieldSet) { + if (is_array($fieldSet)) { + + foreach ($fieldSet as $fieldName => $fieldValue) { + if (isset($this->validationErrors[$fieldName])) { + unset ($this->validationErrors[$fieldName]); + } + + if ($modelName === $this->alias) { + if ($fieldName === $this->primaryKey) { + $this->id = $fieldValue; + } + } + if (is_array($fieldValue) || is_object($fieldValue)) { + $fieldValue = $this->deconstruct($fieldName, $fieldValue); + } + $this->data[$modelName][$fieldName] = $fieldValue; + } + } + } + return $data; + } + +/** + * Normalize Xml::toArray() to use in Model::save() + * + * @param array $xml XML as array + * @return array + */ + protected function _normalizeXmlData(array $xml) { + $return = array(); + foreach ($xml as $key => $value) { + if (is_array($value)) { + $return[Inflector::camelize($key)] = $this->_normalizeXmlData($value); + } elseif ($key[0] === '@') { + $return[substr($key, 1)] = $value; + } else { + $return[$key] = $value; + } + } + return $return; + } + +/** + * Deconstructs a complex data type (array or object) into a single field value. + * + * @param string $field The name of the field to be deconstructed + * @param mixed $data An array or object to be deconstructed into a field + * @return mixed The resulting data that should be assigned to a field + */ + public function deconstruct($field, $data) { + if (!is_array($data)) { + return $data; + } + + $copy = $data; + $type = $this->getColumnType($field); + + if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) { + $useNewDate = (isset($data['year']) || isset($data['month']) || + isset($data['day']) || isset($data['hour']) || isset($data['minute'])); + + $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec'); + $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec'); + $date = array(); + + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) { + $data['hour'] = $data['hour'] + 12; + } + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) { + $data['hour'] = '00'; + } + if ($type == 'time') { + foreach ($timeFields as $key => $val) { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } elseif ($data[$val] === '') { + $data[$val] = ''; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + if (!empty($data[$val])) { + $date[$key] = $data[$val]; + } else { + return null; + } + } + } + + if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') { + foreach ($dateFields as $key => $val) { + if ($val == 'hour' || $val == 'min' || $val == 'sec') { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + } + if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) { + return null; + } + if (isset($data[$val]) && !empty($data[$val])) { + $date[$key] = $data[$val]; + } + } + } + + $format = $this->getDataSource()->columns[$type]['format']; + $day = empty($date['Y']) ? null : $date['Y'] . '-' . $date['m'] . '-' . $date['d'] . ' '; + $hour = empty($date['H']) ? null : $date['H'] . ':' . $date['i'] . ':' . $date['s']; + $date = new DateTime($day . $hour); + if ($useNewDate && !empty($date)) { + return $date->format($format); + } + } + return $data; + } + +/** + * Returns an array of table metadata (column names and types) from the database. + * $field => keys(type, null, default, key, length, extra) + * + * @param mixed $field Set to true to reload schema, or a string to return a specific field + * @return array Array of table metadata + */ + public function schema($field = false) { + if (!is_array($this->_schema) || $field === true) { + $db = $this->getDataSource(); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + if (method_exists($db, 'describe') && $this->useTable !== false) { + $this->_schema = $db->describe($this); + } elseif ($this->useTable === false) { + $this->_schema = array(); + } + } + if (is_string($field)) { + if (isset($this->_schema[$field])) { + return $this->_schema[$field]; + } else { + return null; + } + } + return $this->_schema; + } + +/** + * Returns an associative array of field names and column types. + * + * @return array Field types indexed by field name + */ + public function getColumnTypes() { + $columns = $this->schema(); + if (empty($columns)) { + trigger_error(__d('cake_dev', '(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()'), E_USER_WARNING); + } + $cols = array(); + foreach ($columns as $field => $values) { + $cols[$field] = $values['type']; + } + return $cols; + } + +/** + * Returns the column type of a column in the model. + * + * @param string $column The name of the model column + * @return string Column type + */ + public function getColumnType($column) { + $db = $this->getDataSource(); + $cols = $this->schema(); + $model = null; + + $column = str_replace(array($db->startQuote, $db->endQuote), '', $column); + + if (strpos($column, '.')) { + list($model, $column) = explode('.', $column); + } + if ($model != $this->alias && isset($this->{$model})) { + return $this->{$model}->getColumnType($column); + } + if (isset($cols[$column]) && isset($cols[$column]['type'])) { + return $cols[$column]['type']; + } + return null; + } + +/** + * Returns true if the supplied field exists in the model's database table. + * + * @param mixed $name Name of field to look for, or an array of names + * @param boolean $checkVirtual checks if the field is declared as virtual + * @return mixed If $name is a string, returns a boolean indicating whether the field exists. + * If $name is an array of field names, returns the first field that exists, + * or false if none exist. + */ + public function hasField($name, $checkVirtual = false) { + if (is_array($name)) { + foreach ($name as $n) { + if ($this->hasField($n, $checkVirtual)) { + return $n; + } + } + return false; + } + + if ($checkVirtual && !empty($this->virtualFields)) { + if ($this->isVirtualField($name)) { + return true; + } + } + + if (empty($this->_schema)) { + $this->schema(); + } + + if ($this->_schema != null) { + return isset($this->_schema[$name]); + } + return false; + } + +/** + * Check that a method is callable on a model. This will check both the model's own methods, its + * inherited methods and methods that could be callable through behaviors. + * + * @param string $method The method to be called. + * @return boolean True on method being callable. + */ + public function hasMethod($method) { + if (method_exists($this, $method)) { + return true; + } + if ($this->Behaviors->hasMethod($method)) { + return true; + } + return false; + } + +/** + * Returns true if the supplied field is a model Virtual Field + * + * @param string $field Name of field to look for + * @return boolean indicating whether the field exists as a model virtual field. + */ + public function isVirtualField($field) { + if (empty($this->virtualFields) || !is_string($field)) { + return false; + } + if (isset($this->virtualFields[$field])) { + return true; + } + if (strpos($field, '.') !== false) { + list($model, $field) = explode('.', $field); + if (isset($this->virtualFields[$field])) { + return true; + } + } + return false; + } + +/** + * Returns the expression for a model virtual field + * + * @param string $field Name of field to look for + * @return mixed If $field is string expression bound to virtual field $field + * If $field is null, returns an array of all model virtual fields + * or false if none $field exist. + */ + public function getVirtualField($field = null) { + if ($field == null) { + return empty($this->virtualFields) ? false : $this->virtualFields; + } + if ($this->isVirtualField($field)) { + if (strpos($field, '.') !== false) { + list($model, $field) = explode('.', $field); + } + return $this->virtualFields[$field]; + } + return false; + } + +/** + * Initializes the model for writing a new record, loading the default values + * for those fields that are not defined in $data, and clearing previous validation errors. + * Especially helpful for saving data in loops. + * + * @param mixed $data Optional data array to assign to the model after it is created. If null or false, + * schema data defaults are not merged. + * @param boolean $filterKey If true, overwrites any primary key input with an empty value + * @return array The current Model::data; after merging $data and/or defaults from database + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function create($data = array(), $filterKey = false) { + $defaults = array(); + $this->id = false; + $this->data = array(); + $this->validationErrors = array(); + + if ($data !== null && $data !== false) { + foreach ($this->schema() as $field => $properties) { + if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') { + $defaults[$field] = $properties['default']; + } + } + $this->set($defaults); + $this->set($data); + } + if ($filterKey) { + $this->set($this->primaryKey, false); + } + return $this->data; + } + +/** + * Returns a list of fields from the database, and sets the current model + * data (Model::$data) with the record found. + * + * @param mixed $fields String of single fieldname, or an array of fieldnames. + * @param mixed $id The ID of the record to read + * @return array Array of database fields, or false if not found + * @link http://book.cakephp.org/view/1017/Retrieving-Your-Data#read-1029 + */ + public function read($fields = null, $id = null) { + $this->validationErrors = array(); + + if ($id != null) { + $this->id = $id; + } + + $id = $this->id; + + if (is_array($this->id)) { + $id = $this->id[0]; + } + + if ($id !== null && $id !== false) { + $this->data = $this->find('first', array( + 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), + 'fields' => $fields + )); + return $this->data; + } else { + return false; + } + } + +/** + * Returns the contents of a single field given the supplied conditions, in the + * supplied order. + * + * @param string $name Name of field to get + * @param array $conditions SQL conditions (defaults to NULL) + * @param string $order SQL ORDER BY fragment + * @return string field contents, or false if not found + * @link http://book.cakephp.org/view/1017/Retrieving-Your-Data#field-1028 + */ + public function field($name, $conditions = null, $order = null) { + if ($conditions === null && $this->id !== false) { + $conditions = array($this->alias . '.' . $this->primaryKey => $this->id); + } + if ($this->recursive >= 1) { + $recursive = -1; + } else { + $recursive = $this->recursive; + } + $fields = $name; + if ($data = $this->find('first', compact('conditions', 'fields', 'order', 'recursive'))) { + if (strpos($name, '.') === false) { + if (isset($data[$this->alias][$name])) { + return $data[$this->alias][$name]; + } + } else { + $name = explode('.', $name); + if (isset($data[$name[0]][$name[1]])) { + return $data[$name[0]][$name[1]]; + } + } + if (isset($data[0]) && count($data[0]) > 0) { + return array_shift($data[0]); + } + } else { + return false; + } + } + +/** + * Saves the value of a single field to the database, based on the current + * model ID. + * + * @param string $name Name of the table field + * @param mixed $value Value of the field + * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed + * @return boolean See Model::save() + * @see Model::save() + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function saveField($name, $value, $validate = false) { + $id = $this->id; + $this->create(false); + + if (is_array($validate)) { + $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate); + } else { + $options = array('validate' => $validate, 'fieldList' => array($name)); + } + return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options); + } + +/** + * Saves model data (based on white-list, if supplied) to the database. By + * default, validation occurs before save. + * + * @param array $data Data to save. + * @param mixed $validate Either a boolean, or an array. + * If a boolean, indicates whether or not to validate before saving. + * If an array, allows control of validate, callbacks, and fieldList + * @param array $fieldList List of fields to allow to be written + * @return mixed On success Model::$data if its not empty or true, false on failure + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function save($data = null, $validate = true, $fieldList = array()) { + $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true); + $_whitelist = $this->whitelist; + $fields = array(); + + if (!is_array($validate)) { + $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks')); + } else { + $options = array_merge($defaults, $validate); + } + + if (!empty($options['fieldList'])) { + $this->whitelist = $options['fieldList']; + } elseif ($options['fieldList'] === null) { + $this->whitelist = array(); + } + $this->set($data); + + if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) { + return false; + } + + foreach (array('created', 'updated', 'modified') as $field) { + $keyPresentAndEmpty = ( + isset($this->data[$this->alias]) && + array_key_exists($field, $this->data[$this->alias]) && + $this->data[$this->alias][$field] === null + ); + if ($keyPresentAndEmpty) { + unset($this->data[$this->alias][$field]); + } + } + + $exists = $this->exists(); + $dateFields = array('modified', 'updated'); + + if (!$exists) { + $dateFields[] = 'created'; + } + if (isset($this->data[$this->alias])) { + $fields = array_keys($this->data[$this->alias]); + } + if ($options['validate'] && !$this->validates($options)) { + $this->whitelist = $_whitelist; + return false; + } + + $db = $this->getDataSource(); + + foreach ($dateFields as $updateCol) { + if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) { + $default = array('formatter' => 'date'); + $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]); + if (!array_key_exists('format', $colType)) { + $time = strtotime('now'); + } else { + $time = $colType['formatter']($colType['format']); + } + if (!empty($this->whitelist)) { + $this->whitelist[] = $updateCol; + } + $this->set($updateCol, $time); + } + } + + if ($options['callbacks'] === true || $options['callbacks'] === 'before') { + $result = $this->Behaviors->trigger('beforeSave', array(&$this, $options), array( + 'break' => true, 'breakOn' => array(false, null) + )); + if (!$result || !$this->beforeSave($options)) { + $this->whitelist = $_whitelist; + return false; + } + } + + if (empty($this->data[$this->alias][$this->primaryKey])) { + unset($this->data[$this->alias][$this->primaryKey]); + } + $fields = $values = array(); + + foreach ($this->data as $n => $v) { + if (isset($this->hasAndBelongsToMany[$n])) { + if (isset($v[$n])) { + $v = $v[$n]; + } + $joined[$n] = $v; + } else { + if ($n === $this->alias) { + foreach (array('created', 'updated', 'modified') as $field) { + if (array_key_exists($field, $v) && empty($v[$field])) { + unset($v[$field]); + } + } + + foreach ($v as $x => $y) { + if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) { + list($fields[], $values[]) = array($x, $y); + } + } + } + } + } + $count = count($fields); + + if (!$exists && $count > 0) { + $this->id = false; + } + $success = true; + $created = false; + + if ($count > 0) { + $cache = $this->_prepareUpdateFields(array_combine($fields, $values)); + + if (!empty($this->id)) { + $success = (bool)$db->update($this, $fields, $values); + } else { + $fInfo = $this->schema($this->primaryKey); + $isUUID = ($fInfo['length'] == 36 && + ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary') + ); + if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) { + if (array_key_exists($this->primaryKey, $this->data[$this->alias])) { + $j = array_search($this->primaryKey, $fields); + $values[$j] = String::uuid(); + } else { + list($fields[], $values[]) = array($this->primaryKey, String::uuid()); + } + } + + if (!$db->create($this, $fields, $values)) { + $success = $created = false; + } else { + $created = true; + } + } + + if ($success && !empty($this->belongsTo)) { + $this->updateCounterCache($cache, $created); + } + } + + if (!empty($joined) && $success === true) { + $this->_saveMulti($joined, $this->id, $db); + } + + if ($success && $count > 0) { + if (!empty($this->data)) { + $success = $this->data; + if ($created) { + $this->data[$this->alias][$this->primaryKey] = $this->id; + } + } + if ($options['callbacks'] === true || $options['callbacks'] === 'after') { + $this->Behaviors->trigger('afterSave', array(&$this, $created, $options)); + $this->afterSave($created); + } + if (!empty($this->data)) { + $success = Set::merge($success, $this->data); + } + $this->data = false; + $this->_clearCache(); + $this->validationErrors = array(); + } + $this->whitelist = $_whitelist; + return $success; + } + +/** + * Saves model hasAndBelongsToMany data to the database. + * + * @param array $joined Data to save + * @param mixed $id ID of record in this model + * @param DataSource $db + * @return void + */ + protected function _saveMulti($joined, $id, $db) { + foreach ($joined as $assoc => $data) { + + if (isset($this->hasAndBelongsToMany[$assoc])) { + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + + $keyInfo = $this->{$join}->schema($this->{$join}->primaryKey); + $isUUID = !empty($this->{$join}->primaryKey) && ( + $keyInfo['length'] == 36 && ( + $keyInfo['type'] === 'string' || + $keyInfo['type'] === 'binary' + ) + ); + + $newData = $newValues = array(); + $primaryAdded = false; + + $fields = array( + $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']), + $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']) + ); + + $idField = $db->name($this->{$join}->primaryKey); + if ($isUUID && !in_array($idField, $fields)) { + $fields[] = $idField; + $primaryAdded = true; + } + + foreach ((array)$data as $row) { + if ((is_string($row) && (strlen($row) == 36 || strlen($row) == 16)) || is_numeric($row)) { + $values = array($id, $row); + if ($isUUID && $primaryAdded) { + $values[] = String::uuid(); + } + $newValues[] = $values; + unset($values); + } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + + if ($this->hasAndBelongsToMany[$assoc]['unique']) { + $conditions = array( + $join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id + ); + if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) { + $conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']); + } + $links = $this->{$join}->find('all', array( + 'conditions' => $conditions, + 'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0, + 'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey'] + )); + + $associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey']; + $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}"); + if (!empty($oldLinks)) { + $conditions[$associationForeignKey] = $oldLinks; + $db->delete($this->{$join}, $conditions); + } + } + + if (!empty($newData)) { + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id; + $this->{$join}->create($data); + $this->{$join}->save(); + } + } + + if (!empty($newValues)) { + $db->insertMulti($this->{$join}, $fields, $newValues); + } + } + } + } + +/** + * Updates the counter cache of belongsTo associations after a save or delete operation + * + * @param array $keys Optional foreign key data, defaults to the information $this->data + * @param boolean $created True if a new record was created, otherwise only associations with + * 'counterScope' defined get updated + * @return void + */ + public function updateCounterCache($keys = array(), $created = false) { + $keys = empty($keys) ? $this->data[$this->alias] : $keys; + $keys['old'] = isset($keys['old']) ? $keys['old'] : array(); + + foreach ($this->belongsTo as $parent => $assoc) { + if (!empty($assoc['counterCache'])) { + if (!is_array($assoc['counterCache'])) { + if (isset($assoc['counterScope'])) { + $assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']); + } else { + $assoc['counterCache'] = array($assoc['counterCache'] => array()); + } + } + + $foreignKey = $assoc['foreignKey']; + $fkQuoted = $this->escapeField($assoc['foreignKey']); + + foreach ($assoc['counterCache'] as $field => $conditions) { + if (!is_string($field)) { + $field = Inflector::underscore($this->alias) . '_count'; + } + if (!$this->{$parent}->hasField($field)) { + continue; + } + if ($conditions === true) { + $conditions = array(); + } else { + $conditions = (array)$conditions; + } + + if (!array_key_exists($foreignKey, $keys)) { + $keys[$foreignKey] = $this->field($foreignKey); + } + $recursive = (empty($conditions) ? -1 : 0); + + if (isset($keys['old'][$foreignKey])) { + if ($keys['old'][$foreignKey] != $keys[$foreignKey]) { + $conditions[$fkQuoted] = $keys['old'][$foreignKey]; + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($field => $count), + array($this->{$parent}->escapeField() => $keys['old'][$foreignKey]) + ); + } + } + $conditions[$fkQuoted] = $keys[$foreignKey]; + + if ($recursive === 0) { + $conditions = array_merge($conditions, (array)$conditions); + } + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($field => $count), + array($this->{$parent}->escapeField() => $keys[$foreignKey]) + ); + } + } + } + } + +/** + * Helper method for Model::updateCounterCache(). Checks the fields to be updated for + * + * @param array $data The fields of the record that will be updated + * @return array Returns updated foreign key values, along with an 'old' key containing the old + * values, or empty if no foreign keys are updated. + */ + protected function _prepareUpdateFields($data) { + $foreignKeys = array(); + foreach ($this->belongsTo as $assoc => $info) { + if ($info['counterCache']) { + $foreignKeys[$assoc] = $info['foreignKey']; + } + } + $included = array_intersect($foreignKeys, array_keys($data)); + + if (empty($included) || empty($this->id)) { + return array(); + } + $old = $this->find('first', array( + 'conditions' => array($this->primaryKey => $this->id), + 'fields' => array_values($included), + 'recursive' => -1 + )); + return array_merge($data, array('old' => $old[$this->alias])); + } + +/** + * Backwards compatible passtrough method for: + * saveMany(), validateMany(), saveAssociated() and validateAssociated() + * + * Saves multiple individual records for a single model; Also works with a single record, as well as + * all its associated records. + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * or 'only' to only validate the records, but not save them. + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple + * records of the same type), or an array indexed by association name. + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + * @link http://book.cakephp.org/view/1032/Saving-Related-Model-Data-hasOne-hasMany-belongsTo + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function saveAll($data = null, $options = array()) { + $options = array_merge(array('validate' => 'first'), $options); + if (Set::numeric(array_keys($data))) { + if ($options['validate'] === 'only') { + return $this->validateMany($data, $options); + } + return $this->saveMany($data, $options); + } + if ($options['validate'] === 'only') { + $validatesAssoc = $this->validateAssociated($data, $options); + if (isset($this->validationErrors[$this->alias]) && $this->validationErrors[$this->alias] === false) { + return false; + } + return $validatesAssoc; + } + return $this->saveAssociated($data, $options); + } + +/** + * Saves multiple individual records for a single model + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This should be a numerically-indexed array + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + */ + public function saveMany($data = null, $options = array()) { + if (empty($data)) { + $data = $this->data; + } + + $options = array_merge(array('validate' => 'first', 'atomic' => true), $options); + $this->validationErrors = $validationErrors = array(); + + if (empty($data) && $options['validate'] !== false) { + $result = $this->save($data, $options); + return !empty($result); + } + + if ($options['validate'] === 'first') { + $validates = $this->validateMany($data, $options); + if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { + return $validates; + } + } + + if ($options['atomic']) { + $db = $this->getDataSource(); + $transactionBegun = $db->begin($this); + } + $return = array(); + foreach ($data as $key => $record) { + $validates = ($this->create(null) !== null && $this->save($record, $options)); + if (!$validates) { + $validationErrors[$key] = $this->validationErrors; + } + if (!$options['atomic']) { + $return[] = $validates; + } elseif (!$validates) { + break; + } + } + $this->validationErrors = $validationErrors; + + if (!$options['atomic']) { + return $return; + } + if ($validates) { + if ($transactionBegun) { + return $db->commit($this) !== false; + } else { + return true; + } + } + $db->rollback($this); + return false; + } + +/** + * Validates multiple individual records for a single model + * + * #### Options + * + * - atomic: If true (default), returns boolean. If false returns array. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to validate. This should be a numerically-indexed array + * @param array $options Options to use when validating record data (see above), See also $options of validates(). + * @return boolean True on success, or false on failure. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record validated successfully. + */ + public function validateMany($data, $options = array()) { + $options = array_merge(array('atomic' => true), $options); + $this->validationErrors = $validationErrors = $return = array(); + foreach ($data as $key => $record) { + $validates = $this->create($record) && $this->validates($options); + if (!$validates) { + $validationErrors[$key] = $this->validationErrors; + } + $return[] = $validates; + } + $this->validationErrors = $validationErrors; + if (!$options['atomic']) { + return $return; + } + if (empty($this->validationErrors)) { + return true; + } + return false; + } + +/** + * Saves a single record, as well as all its directly associated records. + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This should be an array indexed by association name. + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + */ + public function saveAssociated($data = null, $options = array()) { + if (empty($data)) { + $data = $this->data; + } + + $options = array_merge(array('validate' => true, 'atomic' => true), $options); + $this->validationErrors = $validationErrors = array(); + + if (empty($data) && $options['validate'] !== false) { + $result = $this->save($data, $options); + return !empty($result); + } + + if ($options['validate'] === 'first') { + $validates = $this->validateAssociated($data, $options); + if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { + return $validates; + } + } + if ($options['atomic']) { + $db = $this->getDataSource(); + $transactionBegun = $db->begin($this); + } + $associations = $this->getAssociated(); + $return = array(); + $validates = true; + foreach ($data as $association => $values) { + if (isset($associations[$association]) && $associations[$association] === 'belongsTo') { + if ($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options)) { + $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id; + } else { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association][] = $validates; + } + } + if ($validates && !($this->create(null) !== null && $this->save($data, $options))) { + $validationErrors[$this->alias] = $this->validationErrors; + $validates = false; + } + $return[$this->alias] = $validates; + + foreach ($data as $association => $values) { + if (!$validates) { + break; + } + if (isset($associations[$association])) { + $type = $associations[$association]; + switch ($type) { + case 'hasOne': + $values[$this->{$type}[$association]['foreignKey']] = $this->id; + if (!($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options))) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association][] = $validates; + break; + case 'hasMany': + foreach ($values as $i => $value) { + $values[$i][$this->{$type}[$association]['foreignKey']] = $this->id; + } + $_return = $this->{$association}->saveMany($values, array_merge($options, array('atomic' => false))); + if (in_array(false, $_return, true)) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association] = $_return; + break; + } + } + } + $this->validationErrors = $validationErrors; + + if (isset($validationErrors[$this->alias])) { + $this->validationErrors = $validationErrors[$this->alias]; + } + + if (!$options['atomic']) { + return $return; + } + if ($validates) { + if ($transactionBegun) { + return $db->commit($this) !== false; + } else { + return true; + } + } + $db->rollback($this); + return false; + } + +/** + * Validates a single record, as well as all its directly associated records. + * + * #### Options + * + * - atomic: If true (default), returns boolean. If false returns array. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to validate. This should be an array indexed by association name. + * @param array $options Options to use when validating record data (see above), See also $options of validates(). + * @return array|boolean If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record validated successfully. + */ + public function validateAssociated($data, $options = array()) { + $options = array_merge(array('atomic' => true), $options); + $this->validationErrors = $validationErrors = $return = array(); + if (!($this->create($data) && $this->validates($options))) { + $validationErrors[$this->alias] = $this->validationErrors; + $return[$this->alias] = false; + } else { + $return[$this->alias] = true; + } + $associations = $this->getAssociated(); + $validates = true; + foreach ($data as $association => $values) { + if (isset($associations[$association])) { + if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { + $validates = $this->{$association}->create($values) && $this->{$association}->validates($options); + $return[$association][] = $validates; + } elseif($associations[$association] === 'hasMany') { + $validates = $this->{$association}->validateMany($values, $options); + $return[$association] = $validates; + } + if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { + $validationErrors[$association] = $this->{$association}->validationErrors; + } + } + } + + $this->validationErrors = $validationErrors; + if (isset($validationErrors[$this->alias])) { + $this->validationErrors = $validationErrors[$this->alias]; + } + if (!$options['atomic']) { + return $return; + } + if ($return[$this->alias] === false || !empty($this->validationErrors)) { + return false; + } + return true; + } + +/** + * Updates multiple model records based on a set of conditions. + * + * @param array $fields Set of fields and values, indexed by fields. + * Fields are treated as SQL snippets, to insert literal values manually escape your data. + * @param mixed $conditions Conditions to match, true for all records + * @return boolean True on success, false on failure + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function updateAll($fields, $conditions = true) { + return $this->getDataSource()->update($this, $fields, null, $conditions); + } + +/** + * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success. + * + * @param mixed $id ID of record to delete + * @param boolean $cascade Set to true to delete records that depend on this record + * @return boolean True on success + * @link http://book.cakephp.org/view/1036/delete + */ + public function delete($id = null, $cascade = true) { + if (!empty($id)) { + $this->id = $id; + } + $id = $this->id; + + if ($this->beforeDelete($cascade)) { + $filters = $this->Behaviors->trigger( + 'beforeDelete', + array(&$this, $cascade), + array('break' => true, 'breakOn' => array(false, null)) + ); + if (!$filters || !$this->exists()) { + return false; + } + $db = $this->getDataSource(); + + $this->_deleteDependent($id, $cascade); + $this->_deleteLinks($id); + $this->id = $id; + + $updateCounterCache = false; + if (!empty($this->belongsTo)) { + foreach ($this->belongsTo as $parent => $assoc) { + if (!empty($assoc['counterCache'])) { + $updateCounterCache = true; + break; + } + } + + $keys = $this->find('first', array( + 'fields' => $this->_collectForeignKeys(), + 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), + 'recursive' => -1, + 'callbacks' => false + )); + } + + if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) { + if ($updateCounterCache) { + $this->updateCounterCache($keys[$this->alias]); + } + $this->Behaviors->trigger('afterDelete', array(&$this)); + $this->afterDelete(); + $this->_clearCache(); + $this->id = false; + return true; + } + } + return false; + } + +/** + * Cascades model deletes through associated hasMany and hasOne child records. + * + * @param string $id ID of record that was deleted + * @param boolean $cascade Set to true to delete records that depend on this record + * @return void + */ + protected function _deleteDependent($id, $cascade) { + if (!empty($this->__backAssociation)) { + $savedAssociatons = $this->__backAssociation; + $this->__backAssociation = array(); + } + foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) { + if ($data['dependent'] === true && $cascade === true) { + + $model = $this->{$assoc}; + $conditions = array($model->escapeField($data['foreignKey']) => $id); + if ($data['conditions']) { + $conditions = array_merge((array)$data['conditions'], $conditions); + } + $model->recursive = -1; + + if (isset($data['exclusive']) && $data['exclusive']) { + $model->deleteAll($conditions); + } else { + $records = $model->find('all', array( + 'conditions' => $conditions, 'fields' => $model->primaryKey + )); + + if (!empty($records)) { + foreach ($records as $record) { + $model->delete($record[$model->alias][$model->primaryKey]); + } + } + } + } + } + if (isset($savedAssociatons)) { + $this->__backAssociation = $savedAssociatons; + } + } + +/** + * Cascades model deletes through HABTM join keys. + * + * @param string $id ID of record that was deleted + * @return void + */ + protected function _deleteLinks($id) { + foreach ($this->hasAndBelongsToMany as $assoc => $data) { + list($plugin, $joinModel) = pluginSplit($data['with']); + $records = $this->{$joinModel}->find('all', array( + 'conditions' => array_merge(array($this->{$joinModel}->escapeField($data['foreignKey']) => $id)), + 'fields' => $this->{$joinModel}->primaryKey, + 'recursive' => -1, + 'callbacks' => false + )); + if (!empty($records)) { + foreach ($records as $record) { + $this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->primaryKey]); + } + } + } + } + +/** + * Deletes multiple model records based on a set of conditions. + * + * @param mixed $conditions Conditions to match + * @param boolean $cascade Set to true to delete records that depend on this record + * @param boolean $callbacks Run callbacks + * @return boolean True on success, false on failure + * @link http://book.cakephp.org/view/1038/deleteAll + */ + public function deleteAll($conditions, $cascade = true, $callbacks = false) { + if (empty($conditions)) { + return false; + } + $db = $this->getDataSource(); + + if (!$cascade && !$callbacks) { + return $db->delete($this, $conditions); + } else { + $ids = $this->find('all', array_merge(array( + 'fields' => "{$this->alias}.{$this->primaryKey}", + 'recursive' => 0), compact('conditions')) + ); + if ($ids === false) { + return false; + } + + $ids = Set::extract($ids, "{n}.{$this->alias}.{$this->primaryKey}"); + if (empty($ids)) { + return true; + } + + if ($callbacks) { + $_id = $this->id; + $result = true; + foreach ($ids as $id) { + $result = ($result && $this->delete($id, $cascade)); + } + $this->id = $_id; + return $result; + } else { + foreach ($ids as $id) { + $this->_deleteLinks($id); + if ($cascade) { + $this->_deleteDependent($id, $cascade); + } + } + return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids)); + } + } + } + +/** + * Collects foreign keys from associations. + * + * @param string $type + * @return array + */ + protected function _collectForeignKeys($type = 'belongsTo') { + $result = array(); + + foreach ($this->{$type} as $assoc => $data) { + if (isset($data['foreignKey']) && is_string($data['foreignKey'])) { + $result[$assoc] = $data['foreignKey']; + } + } + return $result; + } + +/** + * Returns true if a record with the currently set ID exists. + * + * Internally calls Model::getID() to obtain the current record ID to verify, + * and then performs a Model::find('count') on the currently configured datasource + * to ascertain the existence of the record in persistent storage. + * + * @return boolean True if such a record exists + */ + public function exists() { + if ($this->getID() === false) { + return false; + } + $conditions = array($this->alias . '.' . $this->primaryKey => $this->getID()); + $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false); + return ($this->find('count', $query) > 0); + } + +/** + * Returns true if a record that meets given conditions exists. + * + * @param array $conditions SQL conditions array + * @return boolean True if such a record exists + */ + public function hasAny($conditions = null) { + return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false); + } + +/** + * Queries the datasource and returns a result set array. + * + * Also used to perform notation finds, where the first argument is type of find operation to perform + * (all / first / count / neighbors / list / threaded ), + * second parameter options for finding ( indexed array, including: 'conditions', 'limit', + * 'recursive', 'page', 'fields', 'offset', 'order') + * + * Eg: + * {{{ + * find('all', array( + * 'conditions' => array('name' => 'Thomas Anderson'), + * 'fields' => array('name', 'email'), + * 'order' => 'field3 DESC', + * 'recursive' => 2, + * 'group' => 'type' + * )); + * }}} + * + * In addition to the standard query keys above, you can provide Datasource, and behavior specific + * keys. For example, when using a SQL based datasource you can use the joins key to specify additional + * joins that should be part of the query. + * + * {{{ + * find('all', array( + * 'conditions' => array('name' => 'Thomas Anderson'), + * 'joins' => array( + * array( + * 'alias' => 'Thought', + * 'table' => 'thoughts', + * 'type' => 'LEFT', + * 'conditions' => '`Thought`.`person_id` = `Person`.`id`' + * ) + * ) + * )); + * }}} + * + * Behaviors and find types can also define custom finder keys which are passed into find(). + * + * Specifying 'fields' for notation 'list': + * + * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value. + * - If a single field is specified, 'id' is used for key and specified field is used for value. + * - If three fields are specified, they are used (in order) for key, value and group. + * - Otherwise, first and second fields are used for key and value. + * + * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) + * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) + * @return array Array of records + * @link http://book.cakephp.org/view/1018/find + */ + public function find($type = 'first', $query = array()) { + $this->findQueryType = $type; + $this->id = $this->getID(); + + $query = $this->buildQuery($type, $query); + if (is_null($query)) { + return null; + } + + $results = $this->getDataSource()->read($this, $query); + $this->resetAssociations(); + + if ($query['callbacks'] === true || $query['callbacks'] === 'after') { + $results = $this->_filterResults($results); + } + + $this->findQueryType = null; + + if ($type === 'all') { + return $results; + } else { + if ($this->findMethods[$type] === true) { + return $this->{'_find' . ucfirst($type)}('after', $query, $results); + } + } + } + +/** + * Builds the query array that is used by the data source to generate the query to fetch the data. + * + * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) + * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) + * @return array Query array or null if it could not be build for some reasons + * @see Model::find() + */ + public function buildQuery($type = 'first', $query = array()) { + $query = array_merge( + array( + 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null, + 'offset' => null, 'order' => null, 'page' => 1, 'group' => null, 'callbacks' => true, + ), + (array)$query + ); + + if ($type !== 'all') { + if ($this->findMethods[$type] === true) { + $query = $this->{'_find' . ucfirst($type)}('before', $query); + } + } + + if (!is_numeric($query['page']) || intval($query['page']) < 1) { + $query['page'] = 1; + } + if ($query['page'] > 1 && !empty($query['limit'])) { + $query['offset'] = ($query['page'] - 1) * $query['limit']; + } + if ($query['order'] === null && $this->order !== null) { + $query['order'] = $this->order; + } + $query['order'] = array($query['order']); + + if ($query['callbacks'] === true || $query['callbacks'] === 'before') { + $return = $this->Behaviors->trigger( + 'beforeFind', + array(&$this, $query), + array('break' => true, 'breakOn' => array(false, null), 'modParams' => 1) + ); + + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + + $return = $this->beforeFind($query); + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + } + + return $query; + } + +/** + * Handles the before/after filter logic for find('first') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return array + * @see Model::find() + */ + protected function _findFirst($state, $query, $results = array()) { + if ($state === 'before') { + $query['limit'] = 1; + return $query; + } elseif ($state === 'after') { + if (empty($results[0])) { + return false; + } + return $results[0]; + } + } + +/** + * Handles the before/after filter logic for find('count') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return integer The number of records found, or false + * @see Model::find() + */ + protected function _findCount($state, $query, $results = array()) { + if ($state === 'before') { + $db = $this->getDataSource(); + if (empty($query['fields'])) { + $query['fields'] = $db->calculate($this, 'count'); + } elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) { + $query['fields'] = $db->calculate($this, 'count', array( + $db->expression($query['fields']), 'count' + )); + } + $query['order'] = false; + return $query; + } elseif ($state === 'after') { + foreach (array(0, $this->alias) as $key) { + if (isset($results[0][$key]['count'])) { + if (count($results) > 1) { + return intval(array_sum(Set::extract('/' . $key . '/count', $results))); + } else { + return intval($results[0][$key]['count']); + } + } + } + return false; + } + } + +/** + * Handles the before/after filter logic for find('list') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return array Key/value pairs of primary keys/display field values of all records found + * @see Model::find() + */ + protected function _findList($state, $query, $results = array()) { + if ($state === 'before') { + if (empty($query['fields'])) { + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}"); + $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null); + } else { + if (!is_array($query['fields'])) { + $query['fields'] = String::tokenize($query['fields']); + } + + if (count($query['fields']) === 1) { + if (strpos($query['fields'][0], '.') === false) { + $query['fields'][0] = $this->alias . '.' . $query['fields'][0]; + } + + $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null); + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]); + } elseif (count($query['fields']) === 3) { + for ($i = 0; $i < 3; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]); + } else { + for ($i = 0; $i < 2; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null); + } + } + if (!isset($query['recursive']) || $query['recursive'] === null) { + $query['recursive'] = -1; + } + list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list; + return $query; + } elseif ($state === 'after') { + if (empty($results)) { + return array(); + } + $lst = $query['list']; + return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']); + } + } + +/** + * Detects the previous field's value, then uses logic to find the 'wrapping' + * rows and return them. + * + * @param string $state Either "before" or "after" + * @param mixed $query + * @param array $results + * @return array + */ + protected function _findNeighbors($state, $query, $results = array()) { + if ($state === 'before') { + extract($query); + $conditions = (array)$conditions; + if (isset($field) && isset($value)) { + if (strpos($field, '.') === false) { + $field = $this->alias . '.' . $field; + } + } else { + $field = $this->alias . '.' . $this->primaryKey; + $value = $this->id; + } + $query['conditions'] = array_merge($conditions, array($field . ' <' => $value)); + $query['order'] = $field . ' DESC'; + $query['limit'] = 1; + $query['field'] = $field; + $query['value'] = $value; + return $query; + } elseif ($state === 'after') { + extract($query); + unset($query['conditions'][$field . ' <']); + $return = array(); + if (isset($results[0])) { + $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]); + $query['conditions'][$field . ' >='] = $prevVal[0]; + $query['conditions'][$field . ' !='] = $value; + $query['limit'] = 2; + } else { + $return['prev'] = null; + $query['conditions'][$field . ' >'] = $value; + $query['limit'] = 1; + } + $query['order'] = $field . ' ASC'; + $return2 = $this->find('all', $query); + if (!array_key_exists('prev', $return)) { + $return['prev'] = $return2[0]; + } + if (count($return2) === 2) { + $return['next'] = $return2[1]; + } elseif (count($return2) === 1 && !$return['prev']) { + $return['next'] = $return2[0]; + } else { + $return['next'] = null; + } + return $return; + } + } + +/** + * In the event of ambiguous results returned (multiple top level results, with different parent_ids) + * top level results with different parent_ids to the first result will be dropped + * + * @param mixed $state + * @param mixed $query + * @param array $results + * @return array Threaded results + */ + protected function _findThreaded($state, $query, $results = array()) { + if ($state === 'before') { + return $query; + } elseif ($state === 'after') { + $return = $idMap = array(); + $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey); + + foreach ($results as $result) { + $result['children'] = array(); + $id = $result[$this->alias][$this->primaryKey]; + $parentId = $result[$this->alias]['parent_id']; + if (isset($idMap[$id]['children'])) { + $idMap[$id] = array_merge($result, (array)$idMap[$id]); + } else { + $idMap[$id] = array_merge($result, array('children' => array())); + } + if (!$parentId || !in_array($parentId, $ids)) { + $return[] =& $idMap[$id]; + } else { + $idMap[$parentId]['children'][] =& $idMap[$id]; + } + } + if (count($return) > 1) { + $ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return)); + if (count($ids) > 1) { + $root = $return[0][$this->alias]['parent_id']; + foreach ($return as $key => $value) { + if ($value[$this->alias]['parent_id'] != $root) { + unset($return[$key]); + } + } + } + } + return $return; + } + } + +/** + * Passes query results through model and behavior afterFilter() methods. + * + * @param array $results Results to filter + * @param boolean $primary If this is the primary model results (results from model where the find operation was performed) + * @return array Set of filtered results + */ + protected function _filterResults($results, $primary = true) { + $return = $this->Behaviors->trigger( + 'afterFind', + array(&$this, $results, $primary), + array('modParams' => 1) + ); + if ($return !== true) { + $results = $return; + } + return $this->afterFind($results, $primary); + } + +/** + * This resets the association arrays for the model back + * to those originally defined in the model. Normally called at the end + * of each call to Model::find() + * + * @return boolean Success + */ + public function resetAssociations() { + if (!empty($this->__backAssociation)) { + foreach ($this->_associations as $type) { + if (isset($this->__backAssociation[$type])) { + $this->{$type} = $this->__backAssociation[$type]; + } + } + $this->__backAssociation = array(); + } + + foreach ($this->_associations as $type) { + foreach ($this->{$type} as $key => $name) { + if (property_exists($this, $key) && !empty($this->{$key}->__backAssociation)) { + $this->{$key}->resetAssociations(); + } + } + } + $this->__backAssociation = array(); + return true; + } + +/** + * Returns false if any fields passed match any (by default, all if $or = false) of their matching values. + * + * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data) + * @param boolean $or If false, all fields specified must match in order for a false return value + * @return boolean False if any records matching any fields are found + */ + public function isUnique($fields, $or = true) { + if (!is_array($fields)) { + $fields = func_get_args(); + if (is_bool($fields[count($fields) - 1])) { + $or = $fields[count($fields) - 1]; + unset($fields[count($fields) - 1]); + } + } + + foreach ($fields as $field => $value) { + if (is_numeric($field)) { + unset($fields[$field]); + + $field = $value; + if (isset($this->data[$this->alias][$field])) { + $value = $this->data[$this->alias][$field]; + } else { + $value = null; + } + } + + if (strpos($field, '.') === false) { + unset($fields[$field]); + $fields[$this->alias . '.' . $field] = $value; + } + } + if ($or) { + $fields = array('or' => $fields); + } + if (!empty($this->id)) { + $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id; + } + return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0); + } + +/** + * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method. + * + * @param string $sql,... SQL statement + * @return array Resultset + * @link http://book.cakephp.org/view/1027/query + */ + public function query($sql) { + $params = func_get_args(); + $db = $this->getDataSource(); + return call_user_func_array(array(&$db, 'query'), $params); + } + +/** + * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations + * that use the 'with' key as well. Since _saveMulti is incapable of exiting a save operation. + * + * Will validate the currently set data. Use Model::set() or Model::create() to set the active data. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return boolean True if there are no errors + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public function validates($options = array()) { + $errors = $this->invalidFields($options); + if (empty($errors) && $errors !== false) { + $errors = $this->_validateWithModels($options); + } + if (is_array($errors)) { + return count($errors) === 0; + } + return $errors; + } + +/** + * Returns an array of fields that have failed validation. On the current model. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return array Array of invalid fields + * @see Model::validates() + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public function invalidFields($options = array()) { + if ( + !$this->Behaviors->trigger( + 'beforeValidate', + array(&$this, $options), + array('break' => true, 'breakOn' => false) + ) || + $this->beforeValidate($options) === false + ) { + return false; + } + + if (!isset($this->validate) || empty($this->validate)) { + return $this->validationErrors; + } + + $data = $this->data; + $methods = array_map('strtolower', get_class_methods($this)); + $behaviorMethods = array_keys($this->Behaviors->methods()); + + if (isset($data[$this->alias])) { + $data = $data[$this->alias]; + } elseif (!is_array($data)) { + $data = array(); + } + + $exists = $this->exists(); + + $_validate = $this->validate; + $whitelist = $this->whitelist; + + if (!empty($options['fieldList'])) { + $whitelist = $options['fieldList']; + } + + if (!empty($whitelist)) { + $validate = array(); + foreach ((array)$whitelist as $f) { + if (!empty($this->validate[$f])) { + $validate[$f] = $this->validate[$f]; + } + } + $this->validate = $validate; + } + + foreach ($this->validate as $fieldName => $ruleSet) { + if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { + $ruleSet = array($ruleSet); + } + $default = array( + 'allowEmpty' => null, + 'required' => null, + 'rule' => 'blank', + 'last' => true, + 'on' => null + ); + + foreach ($ruleSet as $index => $validator) { + if (!is_array($validator)) { + $validator = array('rule' => $validator); + } + $validator = array_merge($default, $validator); + + $validationDomain = $this->validationDomain; + if (empty($validationDomain)) { + $validationDomain = 'default'; + } + if (isset($validator['message'])) { + $message = $validator['message']; + } else { + $message = __d('cake_dev', 'This field cannot be left blank'); + } + + if ( + empty($validator['on']) || ($validator['on'] == 'create' && + !$exists) || ($validator['on'] == 'update' && $exists + )) { + $required = ( + (!isset($data[$fieldName]) && $validator['required'] === true) || + ( + isset($data[$fieldName]) && (empty($data[$fieldName]) && + !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false + ) + ); + + if ($required) { + $this->invalidate($fieldName, __d($validationDomain, $message)); + if ($validator['last']) { + break; + } + } elseif (array_key_exists($fieldName, $data)) { + if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) { + break; + } + if (is_array($validator['rule'])) { + $rule = $validator['rule'][0]; + unset($validator['rule'][0]); + $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule'])); + } else { + $rule = $validator['rule']; + $ruleParams = array($data[$fieldName]); + } + + $valid = true; + + if (in_array(strtolower($rule), $methods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->dispatchMethod($rule, $ruleParams); + } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); + } elseif (method_exists('Validation', $rule)) { + $valid = call_user_func_array(array('Validation', $rule), $ruleParams); + } elseif (!is_array($validator['rule'])) { + $valid = preg_match($rule, $data[$fieldName]); + } elseif (Configure::read('debug') > 0) { + trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING); + } + + if (!$valid || (is_string($valid) && strlen($valid) > 0)) { + if (is_string($valid) && strlen($valid) > 0) { + $validator['message'] = $valid; + } elseif (!isset($validator['message'])) { + if (is_string($index)) { + $validator['message'] = $index; + } elseif (is_numeric($index) && count($ruleSet) > 1) { + $validator['message'] = $index + 1; + } else { + $validator['message'] = __d($validationDomain, $message); + } + } elseif (is_array($validator['message'])) { + if (count($validator['message']) > 1) { + $args = array_slice($validator['message'], 1); + } else { + $args = $validator['rule']; + } + $validator['message'] = __d($validationDomain, $validator['message'][0], $args); + } + $this->invalidate($fieldName, $validator['message']); + + if ($validator['last']) { + break; + } + } + } + } + } + } + $this->validate = $_validate; + return $this->validationErrors; + } + +/** + * Runs validation for hasAndBelongsToMany associations that have 'with' keys + * set. And data in the set() data set. + * + * @param array $options Array of options to use on Valdation of with models + * @return boolean Failure of validation on with models. + * @see Model::validates() + */ + protected function _validateWithModels($options) { + $valid = true; + foreach ($this->hasAndBelongsToMany as $assoc => $association) { + if (empty($association['with']) || !isset($this->data[$assoc])) { + continue; + } + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + $data = $this->data[$assoc]; + + $newData = array(); + foreach ((array)$data as $row) { + if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + if (empty($newData)) { + continue; + } + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id; + $this->{$join}->create($data); + $valid = ($valid && $this->{$join}->validates($options)); + } + } + return $valid; + } +/** + * Marks a field as invalid, optionally setting the name of validation + * rule (in case of multiple validation for field) that was broken. + * + * @param string $field The name of the field to invalidate + * @param mixed $value Name of validation rule that was not failed, or validation message to + * be returned. If no validation key is provided, defaults to true. + * @return void + */ + public function invalidate($field, $value = true) { + if (!is_array($this->validationErrors)) { + $this->validationErrors = array(); + } + $this->validationErrors[$field] []= $value; + } + +/** + * Returns true if given field name is a foreign key in this model. + * + * @param string $field Returns true if the input string ends in "_id" + * @return boolean True if the field is a foreign key listed in the belongsTo array. + */ + public function isForeignKey($field) { + $foreignKeys = array(); + if (!empty($this->belongsTo)) { + foreach ($this->belongsTo as $assoc => $data) { + $foreignKeys[] = $data['foreignKey']; + } + } + return in_array($field, $foreignKeys); + } + +/** + * Escapes the field name and prepends the model name. Escaping is done according to the + * current database driver's rules. + * + * @param string $field Field to escape (e.g: id) + * @param string $alias Alias for the model (e.g: Post) + * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`). + */ + public function escapeField($field = null, $alias = null) { + if (empty($alias)) { + $alias = $this->alias; + } + if (empty($field)) { + $field = $this->primaryKey; + } + $db = $this->getDataSource(); + if (strpos($field, $db->name($alias) . '.') === 0) { + return $field; + } + return $db->name($alias . '.' . $field); + } + +/** + * Returns the current record's ID + * + * @param integer $list Index on which the composed ID is located + * @return mixed The ID of the current record, false if no ID + */ + public function getID($list = 0) { + if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) { + return false; + } + + if (!is_array($this->id)) { + return $this->id; + } + + if (empty($this->id)) { + return false; + } + + if (isset($this->id[$list]) && !empty($this->id[$list])) { + return $this->id[$list]; + } elseif (isset($this->id[$list])) { + return false; + } + + foreach ($this->id as $id) { + return $id; + } + + return false; + } + +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + */ + public function getLastInsertID() { + return $this->getInsertID(); + } + +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + */ + public function getInsertID() { + return $this->_insertID; + } + +/** + * Sets the ID of the last record this model inserted + * + * @param mixed $id Last inserted ID + * @return void + */ + public function setInsertID($id) { + $this->_insertID = $id; + } + +/** + * Returns the number of rows returned from the last query. + * + * @return integer Number of rows + */ + public function getNumRows() { + return $this->getDataSource()->lastNumRows(); + } + +/** + * Returns the number of rows affected by the last query. + * + * @return integer Number of rows + */ + public function getAffectedRows() { + return $this->getDataSource()->lastAffected(); + } + +/** + * Sets the DataSource to which this model is bound. + * + * @param string $dataSource The name of the DataSource, as defined in app/Config/database.php + * @return boolean True on success + * @throws MissingConnectionException + */ + public function setDataSource($dataSource = null) { + $oldConfig = $this->useDbConfig; + + if ($dataSource != null) { + $this->useDbConfig = $dataSource; + } + $db = ConnectionManager::getDataSource($this->useDbConfig); + if (!empty($oldConfig) && isset($db->config['prefix'])) { + $oldDb = ConnectionManager::getDataSource($oldConfig); + + if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + } elseif (isset($db->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + + if (empty($db) || !is_object($db)) { + throw new MissingConnectionException(array('class' => $this->name)); + } + } + +/** + * Gets the DataSource to which this model is bound. + * + * @return DataSource A DataSource object + */ + public function getDataSource() { + if (!$this->_sourceConfigured && $this->useTable !== false) { + $this->_sourceConfigured = true; + $this->setSource($this->useTable); + } + return ConnectionManager::getDataSource($this->useDbConfig); + } + +/** + * Get associations + * + * @return array + */ + public function associations() { + return $this->_associations; + } + +/** + * Gets all the models with which this model is associated. + * + * @param string $type Only result associations of this type + * @return array Associations + */ + public function getAssociated($type = null) { + if ($type == null) { + $associated = array(); + foreach ($this->_associations as $assoc) { + if (!empty($this->{$assoc})) { + $models = array_keys($this->{$assoc}); + foreach ($models as $m) { + $associated[$m] = $assoc; + } + } + } + return $associated; + } elseif (in_array($type, $this->_associations)) { + if (empty($this->{$type})) { + return array(); + } + return array_keys($this->{$type}); + } else { + $assoc = array_merge( + $this->hasOne, + $this->hasMany, + $this->belongsTo, + $this->hasAndBelongsToMany + ); + if (array_key_exists($type, $assoc)) { + foreach ($this->_associations as $a) { + if (isset($this->{$a}[$type])) { + $assoc[$type]['association'] = $a; + break; + } + } + return $assoc[$type]; + } + return null; + } + } + +/** + * Gets the name and fields to be used by a join model. This allows specifying join fields + * in the association definition. + * + * @param string|array $assoc The model to be joined + * @param array $keys Any join keys which must be merged with the keys queried + * @return array + */ + public function joinModel($assoc, $keys = array()) { + if (is_string($assoc)) { + list(, $assoc) = pluginSplit($assoc); + return array($assoc, array_keys($this->{$assoc}->schema())); + } elseif (is_array($assoc)) { + $with = key($assoc); + return array($with, array_unique(array_merge($assoc[$with], $keys))); + } + trigger_error( + __d('cake_dev', 'Invalid join model settings in %s', $model->alias), + E_USER_WARNING + ); + } + +/** + * Called before each find operation. Return false if you want to halt the find + * call, otherwise return the (modified) query data. + * + * @param array $queryData Data used to execute this query, i.e. conditions, order, etc. + * @return mixed true if the operation should continue, false if it should abort; or, modified + * $queryData to continue with new $queryData + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeFind-1049 + */ + public function beforeFind($queryData) { + return true; + } + +/** + * Called after each find operation. Can be used to modify any results returned by find(). + * Return value should be the (modified) results. + * + * @param mixed $results The results of the find operation + * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association) + * @return mixed Result of the find operation + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterFind-1050 + */ + public function afterFind($results, $primary = false) { + return $results; + } + +/** + * Called before each save operation, after validation. Return a non-true result + * to halt the save. + * + * @param array $options + * @return boolean True if the operation should continue, false if it should abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeSave-1052 + */ + public function beforeSave($options = array()) { + return true; + } + +/** + * Called after each successful save operation. + * + * @param boolean $created True if this save created a new record + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterSave-1053 + */ + public function afterSave($created) { + } + +/** + * Called before every deletion operation. + * + * @param boolean $cascade If true records that depend on this record will also be deleted + * @return boolean True if the operation should continue, false if it should abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeDelete-1054 + */ + public function beforeDelete($cascade = true) { + return true; + } + +/** + * Called after every deletion operation. + * + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterDelete-1055 + */ + public function afterDelete() { + } + +/** + * Called during validation operations, before validation. Please note that custom + * validation rules can be defined in $validate. + * + * @param array $options Options passed from model::save(), see $options of model::save(). + * @return boolean True if validate operation should continue, false to abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeValidate-1051 + */ + public function beforeValidate($options = array()) { + return true; + } + +/** + * Called when a DataSource-level error occurs. + * + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#onError-1056 + */ + public function onError() { + } + +/** + * Clears cache for this model. + * + * @param string $type If null this deletes cached views if Cache.check is true + * Will be used to allow deleting query cache also + * @return boolean true on delete + * @todo + */ + protected function _clearCache($type = null) { + if ($type === null) { + if (Configure::read('Cache.check') === true) { + $assoc[] = strtolower(Inflector::pluralize($this->alias)); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias))); + foreach ($this->_associations as $key => $association) { + foreach ($this->$association as $key => $className) { + $check = strtolower(Inflector::pluralize($className['className'])); + if (!in_array($check, $assoc)) { + $assoc[] = strtolower(Inflector::pluralize($className['className'])); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className']))); + } + } + } + clearCache($assoc); + return true; + } + } else { + //Will use for query cache deleting + } + } +} diff --git a/app/Cake/Model/ModelBehavior.php b/app/Cake/Model/ModelBehavior.php new file mode 100644 index 00000000..ffe52a33 --- /dev/null +++ b/app/Cake/Model/ModelBehavior.php @@ -0,0 +1,220 @@ +Model->doSomething($arg1, $arg2);`. + * + * ### Mapped methods + * + * Behaviors can also define mapped methods. Mapped methods use pattern matching for method invocation. This + * allows you to create methods similar to Model::findAllByXXX methods on your behaviors. Mapped methods need to + * be declared in your behaviors `$mapMethods` array. The method signature for a mapped method is slightly different + * than a normal behavior mixin method. + * + * {{{ + * public $mapMethods = array('/do(\w+)/' => 'doSomething'); + * + * function doSomething($model, $method, $arg1, $arg2) { + * //do something + * } + * }}} + * + * The above will map every doXXX() method call to the behavior. As you can see, the model is + * still the first parameter, but the called method name will be the 2nd parameter. This allows + * you to munge the method name for additional information, much like Model::findAllByXX. + * + * @package Cake.Model + * @see Model::$actsAs + * @see BehaviorCollection::load() + */ +class ModelBehavior extends Object { + +/** + * Contains configuration settings for use with individual model objects. This + * is used because if multiple models use this Behavior, each will use the same + * object instance. Individual model settings should be stored as an + * associative array, keyed off of the model name. + * + * @var array + * @see Model::$alias + */ + public $settings = array(); + +/** + * Allows the mapping of preg-compatible regular expressions to public or + * private methods in this class, where the array key is a /-delimited regular + * expression, and the value is a class method. Similar to the functionality of + * the findBy* / findAllBy* magic methods. + * + * @var array + */ + public $mapMethods = array(); + +/** + * Setup this behavior with the specified configuration settings. + * + * @param Model $model Model using this behavior + * @param array $config Configuration settings for $model + * @return void + */ + public function setup($model, $config = array()) { } + +/** + * Clean up any initialization this behavior has done on a model. Called when a behavior is dynamically + * detached from a model using Model::detach(). + * + * @param Model $model Model using this behavior + * @return void + * @see BehaviorCollection::detach() + */ + public function cleanup($model) { + if (isset($this->settings[$model->alias])) { + unset($this->settings[$model->alias]); + } + } + +/** + * beforeFind can be used to cancel find operations, or modify the query that will be executed. + * By returning null/false you can abort a find. By returning an array you can modify/replace the query + * that is going to be run. + * + * @param Model $model Model using this behavior + * @param array $query Data used to execute this query, i.e. conditions, order, etc. + * @return boolean|array False or null will abort the operation. You can return an array to replace the + * $query that will be eventually run. + */ + public function beforeFind($model, $query) { + return true; + } + +/** + * After find callback. Can be used to modify any results returned by find. + * + * @param Model $model Model using this behavior + * @param mixed $results The results of the find operation + * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association) + * @return mixed An array value will replace the value of $results - any other value will be ignored. + */ + public function afterFind($model, $results, $primary) { } + +/** + * beforeValidate is called before a model is validated, you can use this callback to + * add behavior validation rules into a models validate array. Returning false + * will allow you to make the validation fail. + * + * @param Model $model Model using this behavior + * @return mixed False or null will abort the operation. Any other result will continue. + */ + public function beforeValidate($model) { + return true; + } + +/** + * beforeSave is called before a model is saved. Returning false from a beforeSave callback + * will abort the save operation. + * + * @param Model $model Model using this behavior + * @return mixed False if the operation should abort. Any other result will continue. + */ + public function beforeSave($model) { + return true; + } + +/** + * afterSave is called after a model is saved. + * + * @param Model $model Model using this behavior + * @param boolean $created True if this save created a new record + * @return boolean + */ + public function afterSave($model, $created) { + return true; + } + +/** + * Before delete is called before any delete occurs on the attached model, but after the model's + * beforeDelete is called. Returning false from a beforeDelete will abort the delete. + * + * @param Model $model Model using this behavior + * @param boolean $cascade If true records that depend on this record will also be deleted + * @return mixed False if the operation should abort. Any other result will continue. + */ + public function beforeDelete($model, $cascade = true) { + return true; + } + +/** + * After delete is called after any delete occurs on the attached model. + * + * @param Model $model Model using this behavior + * @return void + */ + public function afterDelete($model) { } + +/** + * DataSource error callback + * + * @param Model $model Model using this behavior + * @param string $error Error generated in DataSource + * @return void + */ + public function onError($model, $error) { } + +/** + * If $model's whitelist property is non-empty, $field will be added to it. + * Note: this method should *only* be used in beforeValidate or beforeSave to ensure + * that it only modifies the whitelist for the current save operation. Also make sure + * you explicitly set the value of the field which you are allowing. + * + * @param Model $model Model using this behavior + * @param string $field Field to be added to $model's whitelist + * @return void + */ + protected function _addToWhitelist($model, $field) { + if (is_array($field)) { + foreach ($field as $f) { + $this->_addToWhitelist($model, $f); + } + return; + } + if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) { + $model->whitelist[] = $field; + } + } +} + diff --git a/app/Cake/Model/Permission.php b/app/Cake/Model/Permission.php new file mode 100644 index 00000000..4ef5e93c --- /dev/null +++ b/app/Cake/Model/Permission.php @@ -0,0 +1,79 @@ +useDbConfig = $config; + } + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/Cake/Network/CakeRequest.php b/app/Cake/Network/CakeRequest.php new file mode 100644 index 00000000..983db7f3 --- /dev/null +++ b/app/Cake/Network/CakeRequest.php @@ -0,0 +1,810 @@ +controller`. + * + * @package Cake.Network + */ +class CakeRequest implements ArrayAccess { +/** + * Array of parameters parsed from the url. + * + * @var array + */ + public $params = array(); + +/** + * Array of POST data. Will contain form data as well as uploaded files. + * Inputs prefixed with 'data' will have the data prefix removed. If there is + * overlap between an input prefixed with data and one without, the 'data' prefixed + * value will take precedence. + * + * @var array + */ + public $data = array(); + +/** + * Array of querystring arguments + * + * @var array + */ + public $query = array(); + +/** + * The url string used for the request. + * + * @var string + */ + public $url; + +/** + * Base url path. + * + * @var string + */ + public $base = false; + +/** + * webroot path segment for the request. + * + * @var string + */ + public $webroot = '/'; + +/** + * The full address to the current request + * + * @var string + */ + public $here = null; + +/** + * The built in detectors used with `is()` can be modified with `addDetector()`. + * + * There are several ways to specify a detector, see CakeRequest::addDetector() for the + * various formats and ways to define detectors. + * + * @var array + */ + protected $_detectors = array( + 'get' => array('env' => 'REQUEST_METHOD', 'value' => 'GET'), + 'post' => array('env' => 'REQUEST_METHOD', 'value' => 'POST'), + 'put' => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'), + 'delete' => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'), + 'head' => array('env' => 'REQUEST_METHOD', 'value' => 'HEAD'), + 'options' => array('env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'), + 'ssl' => array('env' => 'HTTPS', 'value' => 1), + 'ajax' => array('env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'), + 'flash' => array('env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'), + 'mobile' => array('env' => 'HTTP_USER_AGENT', 'options' => array( + 'Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone', + 'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'PalmOS', 'PalmSource', + 'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser', + 'webOS', 'Windows CE', 'Xiino' + )) + ); + +/** + * Copy of php://input. Since this stream can only be read once in most SAPI's + * keep a copy of it so users don't need to know about that detail. + * + * @var string + */ + protected $_input = ''; + +/** + * Constructor + * + * @param string $url Trimmed url string to use. Should not contain the application base path. + * @param boolean $parseEnvironment Set to false to not auto parse the environment. ie. GET, POST and FILES. + */ + public function __construct($url = null, $parseEnvironment = true) { + $this->_base(); + if (empty($url)) { + $url = $this->_url(); + } + if ($url[0] == '/') { + $url = substr($url, 1); + } + $this->url = $url; + + if ($parseEnvironment) { + $this->_processPost(); + $this->_processGet(); + $this->_processFiles(); + } + $this->here = $this->base . '/' . $this->url; + } + +/** + * process the post data and set what is there into the object. + * processed data is available at $this->data + * + * @return void + */ + protected function _processPost() { + $this->data = $_POST; + if (ini_get('magic_quotes_gpc') === '1') { + $this->data = stripslashes_deep($this->data); + } + if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) { + $this->data['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE'); + } + if (isset($this->data['_method'])) { + if (!empty($_SERVER)) { + $_SERVER['REQUEST_METHOD'] = $this->data['_method']; + } else { + $_ENV['REQUEST_METHOD'] = $this->data['_method']; + } + unset($this->data['_method']); + } + if (isset($this->data['data'])) { + $data = $this->data['data']; + unset($this->data['data']); + $this->data = Set::merge($this->data, $data); + } + } + +/** + * Process the GET parameters and move things into the object. + * + * @return void + */ + protected function _processGet() { + if (ini_get('magic_quotes_gpc') === '1') { + $query = stripslashes_deep($_GET); + } else { + $query = $_GET; + } + + unset($query['/' . $this->url]); + if (strpos($this->url, '?') !== false) { + list(, $querystr) = explode('?', $this->url); + parse_str($querystr, $queryArgs); + $query += $queryArgs; + } + if (isset($this->params['url'])) { + $query = array_merge($this->params['url'], $query); + } + $this->query = $query; + } + +/** + * Get the request uri. Looks in PATH_INFO first, as this is the exact value we need prepared + * by PHP. Following that, REQUEST_URI, PHP_SELF, HTTP_X_REWRITE_URL and argv are checked in that order. + * Each of these server variables have the base path, and query strings stripped off + * + * @return string URI The CakePHP request path that is being accessed. + */ + protected function _url() { + if (!empty($_SERVER['PATH_INFO'])) { + return $_SERVER['PATH_INFO']; + } elseif (isset($_SERVER['REQUEST_URI'])) { + $uri = $_SERVER['REQUEST_URI']; + } elseif (isset($_SERVER['PHP_SELF']) && isset($_SERVER['SCRIPT_NAME'])) { + $uri = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['PHP_SELF']); + } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { + $uri = $_SERVER['HTTP_X_REWRITE_URL']; + } elseif ($var = env('argv')) { + $uri = $var[0]; + } + + $base = $this->base; + + if (strlen($base) > 0 && strpos($uri, $base) === 0) { + $uri = substr($uri, strlen($base)); + } + if (strpos($uri, '?') !== false) { + $uri = parse_url($uri, PHP_URL_PATH); + } + if (empty($uri) || $uri == '/' || $uri == '//') { + return '/'; + } + return $uri; + } + +/** + * Returns a base URL and sets the proper webroot + * + * @return string Base URL + */ + protected function _base() { + $dir = $webroot = null; + $config = Configure::read('App'); + extract($config); + + if (empty($base)) { + $base = $this->base; + } + if ($base !== false) { + $this->webroot = $base . '/'; + return $this->base = $base; + } + + if (!$baseUrl) { + $base = dirname(env('SCRIPT_NAME')); + + if ($webroot === 'webroot' && $webroot === basename($base)) { + $base = dirname($base); + } + if ($dir === 'app' && $dir === basename($base)) { + $base = dirname($base); + } + + if ($base === DS || $base === '.') { + $base = ''; + } + + $this->webroot = $base .'/'; + return $this->base = $base; + } + + $file = '/' . basename($baseUrl); + $base = dirname($baseUrl); + + if ($base === DS || $base === '.') { + $base = ''; + } + $this->webroot = $base . '/'; + + $docRoot = env('DOCUMENT_ROOT'); + $docRootContainsWebroot = strpos($docRoot, $dir . '/' . $webroot); + + if (!empty($base) || !$docRootContainsWebroot) { + if (strpos($this->webroot, $dir) === false) { + $this->webroot .= $dir . '/' ; + } + if (strpos($this->webroot, $webroot) === false) { + $this->webroot .= $webroot . '/'; + } + } + return $this->base = $base . $file; + } + +/** + * Process $_FILES and move things into the object. + * + * @return void + */ + protected function _processFiles() { + if (isset($_FILES) && is_array($_FILES)) { + foreach ($_FILES as $name => $data) { + if ($name != 'data') { + $this->params['form'][$name] = $data; + } + } + } + + if (isset($_FILES['data'])) { + foreach ($_FILES['data'] as $key => $data) { + foreach ($data as $model => $fields) { + if (is_array($fields)) { + foreach ($fields as $field => $value) { + if (is_array($value)) { + foreach ($value as $k => $v) { + $this->data[$model][$field][$k][$key] = $v; + } + } else { + $this->data[$model][$field][$key] = $value; + } + } + } else { + $this->data[$model][$key] = $fields; + } + } + } + } + } + +/** + * Get the IP the client is using, or says they are using. + * + * @param boolean $safe Use safe = false when you think the user might manipulate their HTTP_CLIENT_IP + * header. Setting $safe = false will will also look at HTTP_X_FORWARDED_FOR + * @return string The client IP. + */ + public function clientIp($safe = true) { + if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) { + $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR')); + } else { + if (env('HTTP_CLIENT_IP') != null) { + $ipaddr = env('HTTP_CLIENT_IP'); + } else { + $ipaddr = env('REMOTE_ADDR'); + } + } + + if (env('HTTP_CLIENTADDRESS') != null) { + $tmpipaddr = env('HTTP_CLIENTADDRESS'); + + if (!empty($tmpipaddr)) { + $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr); + } + } + return trim($ipaddr); + } + +/** + * Returns the referer that referred this request. + * + * @param boolean $local Attempt to return a local address. Local addresses do not contain hostnames. + * @return string The referring address for this request. + */ + public function referer($local = false) { + $ref = env('HTTP_REFERER'); + $forwarded = env('HTTP_X_FORWARDED_HOST'); + if ($forwarded) { + $ref = $forwarded; + } + + $base = ''; + if (defined('FULL_BASE_URL')) { + $base = FULL_BASE_URL . $this->webroot; + } + if (!empty($ref) && !empty($base)) { + if ($local && strpos($ref, $base) === 0) { + $ref = substr($ref, strlen($base)); + if ($ref[0] != '/') { + $ref = '/' . $ref; + } + return $ref; + } elseif (!$local) { + return $ref; + } + } + return '/'; + } + +/** + * Missing method handler, handles wrapping older style isAjax() type methods + * + * @param string $name The method called + * @param array $params Array of parameters for the method call + * @return mixed + * @throws CakeException when an invalid method is called. + */ + public function __call($name, $params) { + if (strpos($name, 'is') === 0) { + $type = strtolower(substr($name, 2)); + return $this->is($type); + } + throw new CakeException(__d('cake_dev', 'Method %s does not exist', $name)); + } + +/** + * Magic get method allows access to parsed routing parameters directly on the object. + * + * Allows access to `$this->params['controller']` via `$this->controller` + * + * @param string $name The property being accessed. + * @return mixed Either the value of the parameter or null. + */ + public function __get($name) { + if (isset($this->params[$name])) { + return $this->params[$name]; + } + return null; + } + +/** + * Check whether or not a Request is a certain type. Uses the built in detection rules + * as well as additional rules defined with CakeRequest::addDetector(). Any detector can be called + * as `is($type)` or `is$Type()`. + * + * @param string $type The type of request you want to check. + * @return boolean Whether or not the request is the type you are checking. + */ + public function is($type) { + $type = strtolower($type); + if (!isset($this->_detectors[$type])) { + return false; + } + $detect = $this->_detectors[$type]; + if (isset($detect['env'])) { + if (isset($detect['value'])) { + return env($detect['env']) == $detect['value']; + } + if (isset($detect['pattern'])) { + return (bool)preg_match($detect['pattern'], env($detect['env'])); + } + if (isset($detect['options'])) { + $pattern = '/' . implode('|', $detect['options']) . '/i'; + return (bool)preg_match($pattern, env($detect['env'])); + } + } + if (isset($detect['callback']) && is_callable($detect['callback'])) { + return call_user_func($detect['callback'], $this); + } + return false; + } + +/** + * Add a new detector to the list of detectors that a request can use. + * There are several different formats and types of detectors that can be set. + * + * ### Environment value comparison + * + * An environment value comparison, compares a value fetched from `env()` to a known value + * the environment value is equality checked against the provided value. + * + * e.g `addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST'))` + * + * ### Pattern value comparison + * + * Pattern value comparison allows you to compare a value fetched from `env()` to a regular expression. + * + * e.g `addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'));` + * + * ### Option based comparison + * + * Option based comparisons use a list of options to create a regular expression. Subsequent calls + * to add an already defined options detector will merge the options. + * + * e.g `addDetector('mobile', array('env' => 'HTTP_USER_AGENT', 'options' => array('Fennec')));` + * + * ### Callback detectors + * + * Callback detectors allow you to provide a 'callback' type to handle the check. The callback will + * recieve the request object as its only parameter. + * + * e.g `addDetector('custom', array('callback' => array('SomeClass', 'somemethod')));` + * + * @param string $name The name of the detector. + * @param array $options The options for the detector definition. See above. + * @return void + */ + public function addDetector($name, $options) { + if (isset($this->_detectors[$name]) && isset($options['options'])) { + $options = Set::merge($this->_detectors[$name], $options); + } + $this->_detectors[$name] = $options; + } + +/** + * Add parameters to the request's parsed parameter set. This will overwrite any existing parameters. + * This modifies the parameters available through `$request->params`. + * + * @param array $params Array of parameters to merge in + * @return The current object, you can chain this method. + */ + public function addParams($params) { + $this->params = array_merge($this->params, (array)$params); + return $this; + } + +/** + * Add paths to the requests' paths vars. This will overwrite any existing paths. + * Provides an easy way to modify, here, webroot and base. + * + * @param array $paths Array of paths to merge in + * @return CakeRequest the current object, you can chain this method. + */ + public function addPaths($paths) { + foreach (array('webroot', 'here', 'base') as $element) { + if (isset($paths[$element])) { + $this->{$element} = $paths[$element]; + } + } + return $this; + } + +/** + * Get the value of the current requests url. Will include named parameters and querystring arguments. + * + * @param boolean $base Include the base path, set to false to trim the base path off. + * @return string the current request url including query string args. + */ + public function here($base = true) { + $url = $this->here; + if (!empty($this->query)) { + $url .= '?' . http_build_query($this->query); + } + if (!$base) { + $url = preg_replace('/^' . preg_quote($this->base, '/') . '/', '', $url, 1); + } + return $url; + } + +/** + * Read an HTTP header from the Request information. + * + * @param string $name Name of the header you want. + * @return mixed Either false on no header being set or the value of the header. + */ + public static function header($name) { + $name = 'HTTP_' . strtoupper(str_replace('-', '_', $name)); + if (!empty($_SERVER[$name])) { + return $_SERVER[$name]; + } + return false; + } + +/** + * Get the HTTP method used for this request. + * There are a few ways to specify a method. + * + * - If your client supports it you can use native HTTP methods. + * - You can set the HTTP-X-Method-Override header. + * - You can submit an input with the name `_method` + * + * Any of these 3 approaches can be used to set the HTTP method used + * by CakePHP internally, and will effect the result of this method. + * + * @return string The name of the HTTP method used. + */ + public function method() { + return env('REQUEST_METHOD'); + } + +/** + * Get the host that the request was handled on. + * + * @return void + */ + public function host() { + return env('HTTP_HOST'); + } + +/** + * Get the domain name and include $tldLength segments of the tld. + * + * @param integer $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld. + * While `example.co.uk` contains 2. + * @return string Domain name without subdomains. + */ + public function domain($tldLength = 1) { + $segments = explode('.', $this->host()); + $domain = array_slice($segments, -1 * ($tldLength + 1)); + return implode('.', $domain); + } + +/** + * Get the subdomains for a host. + * + * @param integer $tldLength Number of segments your tld contains. For example: `example.com` contains 1 tld. + * While `example.co.uk` contains 2. + * @return array of subdomains. + */ + public function subdomains($tldLength = 1) { + $segments = explode('.', $this->host()); + return array_slice($segments, 0, -1 * ($tldLength + 1)); + } + +/** + * Find out which content types the client accepts or check if they accept a + * particular type of content. + * + * #### Get all types: + * + * `$this->request->accepts();` + * + * #### Check for a single type: + * + * `$this->request->accepts('json');` + * + * This method will order the returned content types by the preference values indicated + * by the client. + * + * @param string $type The content type to check for. Leave null to get all types a client accepts. + * @return mixed Either an array of all the types the client accepts or a boolean if they accept the + * provided type. + */ + public function accepts($type = null) { + $raw = $this->parseAccept(); + $accept = array(); + foreach ($raw as $value => $types) { + $accept = array_merge($accept, $types); + } + if ($type === null) { + return $accept; + } + return in_array($type, $accept); + } + +/** + * Parse the HTTP_ACCEPT header and return a sorted array with content types + * as the keys, and pref values as the values. + * + * Generally you want to use CakeRequest::accept() to get a simple list + * of the accepted content types. + * + * @return array An array of prefValue => array(content/types) + */ + public function parseAccept() { + $accept = array(); + $header = explode(',', $this->header('accept')); + foreach (array_filter($header) as $value) { + $prefPos = strpos($value, ';'); + if ($prefPos !== false) { + $prefValue = substr($value, strpos($value, '=') + 1); + $value = trim(substr($value, 0, $prefPos)); + } else { + $prefValue = '1.0'; + $value = trim($value); + } + if (!isset($accept[$prefValue])) { + $accept[$prefValue] = array(); + } + if ($prefValue) { + $accept[$prefValue][] = $value; + } + } + krsort($accept); + return $accept; + } + +/** + * Get the lanaguages accepted by the client, or check if a specific language is accepted. + * + * Get the list of accepted languages: + * + * {{{ CakeRequest::acceptLanguage(); }}} + * + * Check if a specific language is accepted: + * + * {{{ CakeRequest::acceptLanguage('es-es'); }}} + * + * @param string $language The language to test. + * @return If a $language is provided, a boolean. Otherwise the array of accepted languages. + */ + public static function acceptLanguage($language = null) { + $accepts = preg_split('/[;,]/', self::header('Accept-Language')); + foreach ($accepts as &$accept) { + $accept = strtolower($accept); + if (strpos($accept, '_') !== false) { + $accept = str_replace('_', '-', $accept); + } + } + if ($language === null) { + return $accepts; + } + return in_array($language, $accepts); + } + +/** + * Provides a read/write accessor for `$this->data`. Allows you + * to use a syntax similar to `CakeSession` for reading post data. + * + * ## Reading values. + * + * `$request->data('Post.title');` + * + * When reading values you will get `null` for keys/values that do not exist. + * + * ## Writing values + * + * `$request->data('Post.title', 'New post!');` + * + * You can write to any value, even paths/keys that do not exist, and the arrays + * will be created for you. + * + * @param string $name,... Dot separated name of the value to read/write + * @return mixed Either the value being read, or this so you can chain consecutive writes. + */ + public function data($name) { + $args = func_get_args(); + if (count($args) == 2) { + $this->data = Set::insert($this->data, $name, $args[1]); + return $this; + } + return Set::classicExtract($this->data, $name); + } + +/** + * Read data from `php://stdin`. Useful when interacting with XML or JSON + * request body content. + * + * Getting input with a decoding function: + * + * `$this->request->input('json_decode');` + * + * Getting input using a decoding function, and additional params: + * + * `$this->request->input('Xml::build', array('return' => 'DOMDocument'));` + * + * Any additional parameters are applied to the callback in the order they are given. + * + * @param string $callback A decoding callback that will convert the string data to another + * representation. Leave empty to access the raw input data. You can also + * supply additional parameters for the decoding callback using var args, see above. + * @return The decoded/processed request data. + */ + public function input($callback = null) { + $input = $this->_readStdin(); + $args = func_get_args(); + if (!empty($args)) { + $callback = array_shift($args); + array_unshift($args, $input); + return call_user_func_array($callback, $args); + } + return $input; + } + +/** + * Read data from php://stdin, mocked in tests. + * + * @return string contents of stdin + */ + protected function _readStdin() { + if (empty($this->_input)) { + $fh = fopen('php://input', 'r'); + $content = stream_get_contents($fh); + fclose($fh); + $this->_input = $content; + } + return $this->_input; + } + +/** + * Array access read implementation + * + * @param string $name Name of the key being accessed. + * @return mixed + */ + public function offsetGet($name) { + if (isset($this->params[$name])) { + return $this->params[$name]; + } + if ($name == 'url') { + return $this->query; + } + if ($name == 'data') { + return $this->data; + } + return null; + } + +/** + * Array access write implementation + * + * @param string $name Name of the key being written + * @param mixed $value The value being written. + * @return void + */ + public function offsetSet($name, $value) { + $this->params[$name] = $value; + } + +/** + * Array access isset() implementation + * + * @param string $name thing to check. + * @return boolean + */ + public function offsetExists($name) { + return isset($this->params[$name]); + } + +/** + * Array access unset() implementation + * + * @param string $name Name to unset. + * @return void + */ + public function offsetUnset($name) { + unset($this->params[$name]); + } +} diff --git a/app/Cake/Network/CakeResponse.php b/app/Cake/Network/CakeResponse.php new file mode 100644 index 00000000..d917bc81 --- /dev/null +++ b/app/Cake/Network/CakeResponse.php @@ -0,0 +1,665 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out' + ); + +/** + * Holds known mime type mappings + * + * @var array + */ + protected $_mimeTypes = array( + 'ai' => 'application/postscript', + 'bcpio' => 'application/x-bcpio', + 'bin' => 'application/octet-stream', + 'ccad' => 'application/clariscad', + 'cdf' => 'application/x-netcdf', + 'class' => 'application/octet-stream', + 'cpio' => 'application/x-cpio', + 'cpt' => 'application/mac-compactpro', + 'csh' => 'application/x-csh', + 'csv' => array('text/csv', 'application/vnd.ms-excel', 'text/plain'), + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dms' => 'application/octet-stream', + 'doc' => 'application/msword', + 'drw' => 'application/drafting', + 'dvi' => 'application/x-dvi', + 'dwg' => 'application/acad', + 'dxf' => 'application/dxf', + 'dxr' => 'application/x-director', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'exe' => 'application/octet-stream', + 'ez' => 'application/andrew-inset', + 'flv' => 'video/x-flv', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'bz2' => 'application/x-bzip', + '7z' => 'application/x-7z-compressed', + 'hdf' => 'application/x-hdf', + 'hqx' => 'application/mac-binhex40', + 'ico' => 'image/vnd.microsoft.icon', + 'ips' => 'application/x-ipscript', + 'ipx' => 'application/x-ipix', + 'js' => 'text/javascript', + 'latex' => 'application/x-latex', + 'lha' => 'application/octet-stream', + 'lsp' => 'application/x-lisp', + 'lzh' => 'application/octet-stream', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'mif' => 'application/vnd.mif', + 'ms' => 'application/x-troff-ms', + 'nc' => 'application/x-netcdf', + 'oda' => 'application/oda', + 'otf' => 'font/otf', + 'pdf' => 'application/pdf', + 'pgn' => 'application/x-chess-pgn', + 'pot' => 'application/mspowerpoint', + 'pps' => 'application/mspowerpoint', + 'ppt' => 'application/mspowerpoint', + 'ppz' => 'application/mspowerpoint', + 'pre' => 'application/x-freelance', + 'prt' => 'application/pro_eng', + 'ps' => 'application/postscript', + 'roff' => 'application/x-troff', + 'scm' => 'application/x-lotusscreencam', + 'set' => 'application/set', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'sit' => 'application/x-stuffit', + 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'skp' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'sol' => 'application/solids', + 'spl' => 'application/x-futuresplash', + 'src' => 'application/x-wais-source', + 'step' => 'application/STEP', + 'stl' => 'application/SLA', + 'stp' => 'application/STEP', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 't' => 'application/x-troff', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'tr' => 'application/x-troff', + 'tsp' => 'application/dsptype', + 'ttf' => 'font/ttf', + 'unv' => 'application/i-deas', + 'ustar' => 'application/x-ustar', + 'vcd' => 'application/x-cdlink', + 'vda' => 'application/vda', + 'xlc' => 'application/vnd.ms-excel', + 'xll' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlw' => 'application/vnd.ms-excel', + 'zip' => 'application/zip', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'au' => 'audio/basic', + 'kar' => 'audio/midi', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', + 'ra' => 'audio/x-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'snd' => 'audio/basic', + 'tsi' => 'audio/TSP-audio', + 'wav' => 'audio/x-wav', + 'asc' => 'text/plain', + 'c' => 'text/plain', + 'cc' => 'text/plain', + 'css' => 'text/css', + 'etx' => 'text/x-setext', + 'f' => 'text/plain', + 'f90' => 'text/plain', + 'h' => 'text/plain', + 'hh' => 'text/plain', + 'html' => array('text/html', '*/*'), + 'htm' => array('text/html', '*/*'), + 'm' => 'text/plain', + 'rtf' => 'text/rtf', + 'rtx' => 'text/richtext', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'tsv' => 'text/tab-separated-values', + 'tpl' => 'text/template', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'xml' => array('application/xml', 'text/xml'), + 'avi' => 'video/x-msvideo', + 'fli' => 'video/x-fli', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'viv' => 'video/vnd.vivo', + 'vivo' => 'video/vnd.vivo', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'ppm' => 'image/x-portable-pixmap', + 'ras' => 'image/cmu-raster', + 'rgb' => 'image/x-rgb', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'ice' => 'x-conference/x-cooltalk', + 'iges' => 'model/iges', + 'igs' => 'model/iges', + 'mesh' => 'model/mesh', + 'msh' => 'model/mesh', + 'silo' => 'model/mesh', + 'vrml' => 'model/vrml', + 'wrl' => 'model/vrml', + 'mime' => 'www/mime', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-pdb', + 'javascript' => 'text/javascript', + 'json' => 'application/json', + 'form' => 'application/x-www-form-urlencoded', + 'file' => 'multipart/form-data', + 'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'), + 'xhtml-mobile' => 'application/vnd.wap.xhtml+xml', + 'rss' => 'application/rss+xml', + 'atom' => 'application/atom+xml', + 'amf' => 'application/x-amf', + 'wap' => array('text/vnd.wap.wml', 'text/vnd.wap.wmlscript', 'image/vnd.wap.wbmp'), + 'wml' => 'text/vnd.wap.wml', + 'wmlscript' => 'text/vnd.wap.wmlscript', + 'wbmp' => 'image/vnd.wap.wbmp', + ); + +/** + * Protocol header to send to the client + * + * @var string + */ + protected $_protocol = 'HTTP/1.1'; + +/** + * Status code to send to the client + * + * @var integer + */ + protected $_status = 200; + +/** + * Content type to send. This can be an 'extension' that will be transformed using the $_mimetypes array + * or a complete mime-type + * + * @var integer + */ + protected $_contentType = 'text/html'; + +/** + * Buffer list of headers + * + * @var array + */ + protected $_headers = array(); + +/** + * Buffer string for response message + * + * @var string + */ + protected $_body = null; + +/** + * The charset the response body is encoded with + * + * @var string + */ + protected $_charset = 'UTF-8'; + +/** + * Class constructor + * + * @param array $options list of parameters to setup the response. Possible values are: + * - body: the rensonse text that should be sent to the client + * - status: the HTTP status code to respond with + * - type: a complete mime-type string or an extension mapepd in this class + * - charset: the charset for the response body + */ + public function __construct(array $options = array()) { + if (isset($options['body'])) { + $this->body($options['body']); + } + if (isset($options['status'])) { + $this->statusCode($options['status']); + } + if (isset($options['type'])) { + $this->type($options['type']); + } + if (isset($options['charset'])) { + $this->charset($options['charset']); + } + } + +/** + * Sends the complete response to the client including headers and message body. + * Will echo out the content in the response body. + * + * @return void + */ + public function send() { + if (isset($this->_headers['Location']) && $this->_status === 200) { + $this->statusCode(302); + } + + $codeMessage = $this->_statusCodes[$this->_status]; + $this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}"); + $this->_sendHeader('Content-Type', "{$this->_contentType}; charset={$this->_charset}"); + + foreach ($this->_headers as $header => $value) { + $this->_sendHeader($header, $value); + } + $this->_sendContent($this->_body); + } + +/** + * Sends a header to the client. + * + * @param string $name the header name + * @param string $value the header value + * @return void + */ + protected function _sendHeader($name, $value = null) { + if (!headers_sent()) { + if (is_null($value)) { + header($name); + } else { + header("{$name}: {$value}"); + } + } + } + +/** + * Sends a content string to the client. + * + * @param string $content string to send as response body + * @return void + */ + protected function _sendContent($content) { + echo $content; + } + +/** + * Buffers a header string to be sent + * Returns the complete list of buffered headers + * + * ### Single header + * e.g `header('Location', 'http://example.com');` + * + * ### Multiple headers + * e.g `header(array('Location' => 'http://example.com', 'X-Extra' => 'My header'));` + * + * ### String header + * e.g `header('WWW-Authenticate: Negotiate');` + * + * ### Array of string headers + * e.g `header(array('WWW-Authenticate: Negotiate', 'Content-type: application/pdf'));` + * + * Multiple calls for setting the same header name will have the same effect as setting the header once + * with the last value sent for it + * e.g `header('WWW-Authenticate: Negotiate'); header('WWW-Authenticate: Not-Negotiate');` + * will have the same effect as only doing `header('WWW-Authenticate: Not-Negotiate');` + * + * @param mixed $header. An array of header strings or a single header string + * - an assotiative array of "header name" => "header value" is also accepted + * - an array of string headers is also accepted + * @param mixed $value. The header value. + * @return array list of headers to be sent + */ + public function header($header = null, $value = null) { + if (is_null($header)) { + return $this->_headers; + } + if (is_array($header)) { + foreach ($header as $h => $v) { + if (is_numeric($h)) { + $this->header($v); + continue; + } + $this->_headers[$h] = trim($v); + } + return $this->_headers; + } + + if (!is_null($value)) { + $this->_headers[$header] = $value; + return $this->_headers; + } + + list($header, $value) = explode(':', $header, 2); + $this->_headers[$header] = trim($value); + return $this->_headers; + } + +/** + * Buffers the response message to be sent + * if $content is null the current buffer is returned + * + * @param string $content the string message to be sent + * @return string current message buffer if $content param is passed as null + */ + public function body($content = null) { + if (is_null($content)) { + return $this->_body; + } + return $this->_body = $content; + } + +/** + * Sets the HTTP status code to be sent + * if $code is null the current code is returned + * + * @param integer $code + * @return integer current status code + * @throws CakeException When an unknown status code is reached. + */ + public function statusCode($code = null) { + if (is_null($code)) { + return $this->_status; + } + if (!isset($this->_statusCodes[$code])) { + throw new CakeException(__d('cake_dev', 'Unknown status code')); + } + return $this->_status = $code; + } + +/** + * Queries & sets valid HTTP response codes & messages. + * + * @param mixed $code If $code is an integer, then the corresponding code/message is + * returned if it exists, null if it does not exist. If $code is an array, + * then the 'code' and 'message' keys of each nested array are added to the default + * HTTP codes. Example: + * + * httpCodes(404); // returns array(404 => 'Not Found') + * + * httpCodes(array( + * 701 => 'Unicorn Moved', + * 800 => 'Unexpected Minotaur' + * )); // sets these new values, and returns true + * + * @return mixed associative array of the HTTP codes as keys, and the message + * strings as values, or null of the given $code does not exist. + */ + public function httpCodes($code = null) { + if (empty($code)) { + return $this->_statusCodes; + } + + if (is_array($code)) { + $this->_statusCodes = $code + $this->_statusCodes; + return true; + } + + if (!isset($this->_statusCodes[$code])) { + return null; + } + return array($code => $this->_statusCodes[$code]); + } + +/** + * Sets the response content type. It can be either a file extension + * which will be mapped internally to a mime-type or a string representing a mime-type + * if $contentType is null the current content type is returned + * if $contentType is an associative array, it will be stored as a content type definition + * + * ### Setting the content type + * + * e.g `type('jpg');` + * + * ### Returning the current content type + * + * e.g `type();` + * + * ### Storing a content type definition + * + * e.g `type(array('keynote' => 'application/keynote'));` + * + * ### Replacing a content type definition + * + * e.g `type(array('jpg' => 'text/plain'));` + * + * @param string $contentType + * @return mixed current content type or false if supplied an invalid content type + */ + public function type($contentType = null) { + if (is_null($contentType)) { + return $this->_contentType; + } + if (is_array($contentType)) { + $type = key($contentType); + $defitition = current($contentType); + $this->_mimeTypes[$type] = $defitition; + return $this->_contentType; + } + if (isset($this->_mimeTypes[$contentType])) { + $contentType = $this->_mimeTypes[$contentType]; + $contentType = is_array($contentType) ? current($contentType) : $contentType; + } + if (strpos($contentType, '/') === false) { + return false; + } + return $this->_contentType = $contentType; + } + +/** + * Returns the mime type definition for an alias + * + * e.g `getMimeType('pdf'); // returns 'application/pdf'` + * + * @param string $alias the content type alias to map + * @return mixed string mapped mime type or false if $alias is not mapped + */ + public function getMimeType($alias) { + if (isset($this->_mimeTypes[$alias])) { + return $this->_mimeTypes[$alias]; + } + return false; + } + +/** + * Maps a content-type back to an alias + * + * e.g `mapType('application/pdf'); // returns 'pdf'` + * + * @param mixed $ctype Either a string content type to map, or an array of types. + * @return mixed Aliases for the types provided. + */ + public function mapType($ctype) { + if (is_array($ctype)) { + return array_map(array($this, 'mapType'), $ctype); + } + + foreach ($this->_mimeTypes as $alias => $types) { + if (is_array($types) && in_array($ctype, $types)) { + return $alias; + } elseif (is_string($types) && $types == $ctype) { + return $alias; + } + } + return null; + } + +/** + * Sets the response charset + * if $charset is null the current charset is returned + * + * @param string $charset + * @return string current charset + */ + public function charset($charset = null) { + if (is_null($charset)) { + return $this->_charset; + } + return $this->_charset = $charset; + } + +/** + * Sets the correct headers to instruct the client to not cache the response + * + * @return void + */ + public function disableCache() { + $this->header(array( + 'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', + 'Last-Modified' => gmdate("D, d M Y H:i:s") . " GMT", + 'Cache-Control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', + 'Pragma' => 'no-cache' + )); + } + +/** + * Sets the correct headers to instruct the client to cache the response. + * + * @param string $since a valid time since the response text has not been modified + * @param string $time a valid time for cache expiry + * @return void + */ + public function cache($since, $time = '+1 day') { + if (!is_integer($time)) { + $time = strtotime($time); + } + $this->header(array( + 'Date' => gmdate("D, j M Y G:i:s ", time()) . 'GMT', + 'Last-Modified' => gmdate("D, j M Y G:i:s ", $since) . 'GMT', + 'Expires' => gmdate("D, j M Y H:i:s", $time) . " GMT", + 'Cache-Control' => 'public, max-age=' . ($time - time()), + 'Pragma' => 'cache' + )); + } + +/** + * Sets the correct output buffering handler to send a compressed response. Responses will + * be compressed with zlib, if the extension is available. + * + * @return boolean false if client does not accept compressed responses or no handler is available, true otherwise + */ + public function compress() { + $compressionEnabled = ini_get("zlib.output_compression") !== '1' && + extension_loaded("zlib") && + (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false); + return $compressionEnabled && ob_start('ob_gzhandler'); + } + +/** + * Sets the correct headers to instruct the browser to dowload the response as a file. + * + * @param string $filename the name of the file as the browser will download the response + * @return void + */ + public function download($filename) { + $this->header('Content-Disposition', 'attachment; filename="' . $filename . '"'); + } + +/** + * String conversion. Fetches the response body as a string. + * Does *not* send headers. + * + * @return string + */ + public function __toString() { + return (string)$this->_body; + } +} diff --git a/app/Cake/Network/CakeSocket.php b/app/Cake/Network/CakeSocket.php new file mode 100644 index 00000000..377b54cb --- /dev/null +++ b/app/Cake/Network/CakeSocket.php @@ -0,0 +1,278 @@ + false, + 'host' => 'localhost', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30 + ); + +/** + * Configuration settings for the socket connection + * + * @var array + */ + public $config = array(); + +/** + * Reference to socket connection resource + * + * @var resource + */ + public $connection = null; + +/** + * This boolean contains the current state of the CakeSocket class + * + * @var boolean + */ + public $connected = false; + +/** + * This variable contains an array with the last error number (num) and string (str) + * + * @var array + */ + public $lastError = array(); + +/** + * Constructor. + * + * @param array $config Socket configuration, which will be merged with the base configuration + * @see CakeSocket::$_baseConfig + */ + public function __construct($config = array()) { + $this->config = array_merge($this->_baseConfig, $config); + if (!is_numeric($this->config['protocol'])) { + $this->config['protocol'] = getprotobyname($this->config['protocol']); + } + } + +/** + * Connect the socket to the given host and port. + * + * @return boolean Success + * @throws SocketException + */ + public function connect() { + if ($this->connection != null) { + $this->disconnect(); + } + + $scheme = null; + if (isset($this->config['request']) && $this->config['request']['uri']['scheme'] == 'https') { + $scheme = 'ssl://'; + } + + if ($this->config['persistent'] == true) { + $this->connection = @pfsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); + } else { + $this->connection = @fsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']); + } + + if (!empty($errNum) || !empty($errStr)) { + $this->setLastError($errNum, $errStr); + throw new SocketException($errStr, $errNum); + } + + $this->connected = is_resource($this->connection); + if ($this->connected) { + stream_set_timeout($this->connection, $this->config['timeout']); + } + return $this->connected; + } + +/** + * Get the host name of the current connection. + * + * @return string Host name + */ + public function host() { + if (Validation::ip($this->config['host'])) { + return gethostbyaddr($this->config['host']); + } + return gethostbyaddr($this->address()); + } + +/** + * Get the IP address of the current connection. + * + * @return string IP address + */ + public function address() { + if (Validation::ip($this->config['host'])) { + return $this->config['host']; + } + return gethostbyname($this->config['host']); + } + +/** + * Get all IP addresses associated with the current connection. + * + * @return array IP addresses + */ + public function addresses() { + if (Validation::ip($this->config['host'])) { + return array($this->config['host']); + } + return gethostbynamel($this->config['host']); + } + +/** + * Get the last error as a string. + * + * @return string Last error + */ + public function lastError() { + if (!empty($this->lastError)) { + return $this->lastError['num'] . ': ' . $this->lastError['str']; + } + return null; + } + +/** + * Set the last error. + * + * @param integer $errNum Error code + * @param string $errStr Error string + * @return void + */ + public function setLastError($errNum, $errStr) { + $this->lastError = array('num' => $errNum, 'str' => $errStr); + } + +/** + * Write data to the socket. + * + * @param string $data The data to write to the socket + * @return boolean Success + */ + public function write($data) { + if (!$this->connected) { + if (!$this->connect()) { + return false; + } + } + $totalBytes = strlen($data); + for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) { + $rv = fwrite($this->connection, substr($data, $written)); + if ($rv === false || $rv === 0) { + return $written; + } + } + return $written; + } + +/** + * Read data from the socket. Returns false if no data is available or no connection could be + * established. + * + * @param integer $length Optional buffer length to read; defaults to 1024 + * @return mixed Socket data + */ + public function read($length = 1024) { + if (!$this->connected) { + if (!$this->connect()) { + return false; + } + } + + if (!feof($this->connection)) { + $buffer = fread($this->connection, $length); + $info = stream_get_meta_data($this->connection); + if ($info['timed_out']) { + $this->setLastError(E_WARNING, __d('cake_dev', 'Connection timed out')); + return false; + } + return $buffer; + } + return false; + } + +/** + * Disconnect the socket from the current connection. + * + * @return boolean Success + */ + public function disconnect() { + if (!is_resource($this->connection)) { + $this->connected = false; + return true; + } + $this->connected = !fclose($this->connection); + + if (!$this->connected) { + $this->connection = null; + } + return !$this->connected; + } + +/** + * Destructor, used to disconnect from current connection. + * + */ + public function __destruct() { + $this->disconnect(); + } + +/** + * Resets the state of this Socket instance to it's initial state (before Object::__construct got executed) + * + * @param array $state Array with key and values to reset + * @return boolean True on success + */ + public function reset($state = null) { + if (empty($state)) { + static $initalState = array(); + if (empty($initalState)) { + $initalState = get_class_vars(__CLASS__); + } + $state = $initalState; + } + + foreach ($state as $property => $value) { + $this->{$property} = $value; + } + return true; + } +} diff --git a/app/Cake/Network/Email/AbstractTransport.php b/app/Cake/Network/Email/AbstractTransport.php new file mode 100644 index 00000000..e66e2e9b --- /dev/null +++ b/app/Cake/Network/Email/AbstractTransport.php @@ -0,0 +1,76 @@ +_config = $config; + } + return $this->_config; + } + +/** + * Help to convert headers in string + * + * @param array $headers Headers in format key => value + * @param string $eol + * @return string + */ + protected function _headersToString($headers, $eol = "\r\n") { + $out = ''; + foreach ($headers as $key => $value) { + if ($value === false || $value === null || $value === '') { + continue; + } + $out .= $key . ': ' . $value . $eol; + } + if (!empty($out)) { + $out = substr($out, 0, -1 * strlen($eol)); + } + return $out; + } + +} diff --git a/app/Cake/Network/Email/CakeEmail.php b/app/Cake/Network/Email/CakeEmail.php new file mode 100644 index 00000000..679d300c --- /dev/null +++ b/app/Cake/Network/Email/CakeEmail.php @@ -0,0 +1,1340 @@ +charset = $charset; + } + if ($config) { + $this->config($config); + } + } + +/** + * From + * + * @param mixed $email + * @param string $name + * @return mixed + * @throws SocketException + */ + public function from($email = null, $name = null) { + if ($email === null) { + return $this->_from; + } + return $this->_setEmailSingle('_from', $email, $name, __d('cake', 'From requires only 1 email address.')); + } + +/** + * Sender + * + * @param mixed $email + * @param string $name + * @return mixed + * @throws SocketException + */ + public function sender($email = null, $name = null) { + if ($email === null) { + return $this->_sender; + } + return $this->_setEmailSingle('_sender', $email, $name, __d('cake', 'Sender requires only 1 email address.')); + } + +/** + * Reply-To + * + * @param mixed $email + * @param string $name + * @return mixed + * @throws SocketException + */ + public function replyTo($email = null, $name = null) { + if ($email === null) { + return $this->_replyTo; + } + return $this->_setEmailSingle('_replyTo', $email, $name, __d('cake', 'Reply-To requires only 1 email address.')); + } + +/** + * Read Receipt (Disposition-Notification-To header) + * + * @param mixed $email + * @param string $name + * @return mixed + * @throws SocketException + */ + public function readReceipt($email = null, $name = null) { + if ($email === null) { + return $this->_readReceipt; + } + return $this->_setEmailSingle('_readReceipt', $email, $name, __d('cake', 'Disposition-Notification-To requires only 1 email address.')); + } + +/** + * Return Path + * + * @param mixed $email + * @param string $name + * @return mixed + * @throws SocketException + */ + public function returnPath($email = null, $name = null) { + if ($email === null) { + return $this->_returnPath; + } + return $this->_setEmailSingle('_returnPath', $email, $name, __d('cake', 'Return-Path requires only 1 email address.')); + } + +/** + * To + * + * @param mixed $email Null to get, String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return mixed + */ + public function to($email = null, $name = null) { + if ($email === null) { + return $this->_to; + } + return $this->_setEmail('_to', $email, $name); + } + +/** + * Add To + * + * @param mixed $email String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return CakeEmail $this + */ + public function addTo($email, $name = null) { + return $this->_addEmail('_to', $email, $name); + } + +/** + * Cc + * + * @param mixed $email String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return mixed + */ + public function cc($email = null, $name = null) { + if ($email === null) { + return $this->_cc; + } + return $this->_setEmail('_cc', $email, $name); + } + +/** + * Add Cc + * + * @param mixed $email String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return CakeEmail $this + */ + public function addCc($email, $name = null) { + return $this->_addEmail('_cc', $email, $name); + } + +/** + * Bcc + * + * @param mixed $email String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return mixed + */ + public function bcc($email = null, $name = null) { + if ($email === null) { + return $this->_bcc; + } + return $this->_setEmail('_bcc', $email, $name); + } + +/** + * Add Bcc + * + * @param mixed $email String with email, Array with email as key, name as value or email as value (without name) + * @param string $name + * @return CakeEmail $this + */ + public function addBcc($email, $name = null) { + return $this->_addEmail('_bcc', $email, $name); + } + +/** + * Set email + * + * @param string $varName + * @param mixed $email + * @param mixed $name + * @return CakeEmail $this + * @throws SocketException + */ + protected function _setEmail($varName, $email, $name) { + if (!is_array($email)) { + if (!Validation::email($email)) { + throw new SocketException(__d('cake', 'Invalid email: "%s"', $email)); + } + if ($name === null) { + $name = $email; + } + $this->{$varName} = array($email => $name); + return $this; + } + $list = array(); + foreach ($email as $key => $value) { + if (is_int($key)) { + $key = $value; + } + if (!Validation::email($key)) { + throw new SocketException(__d('cake', 'Invalid email: "%s"', $key)); + } + $list[$key] = $value; + } + $this->{$varName} = $list; + return $this; + } + +/** + * Set only 1 email + * + * @param string $varName + * @param mixed $email + * @param string $name + * @param string $throwMessage + * @return CakeEmail $this + * @throws SocketException + */ + protected function _setEmailSingle($varName, $email, $name, $throwMessage) { + $current = $this->{$varName}; + $this->_setEmail($varName, $email, $name); + if (count($this->{$varName}) !== 1) { + $this->{$varName} = $current; + throw new SocketException($throwMessage); + } + return $this; + } + +/** + * Add email + * + * @param string $varName + * @param mixed $email + * @param mixed $name + * @return CakeEmail $this + * @throws SocketException + */ + protected function _addEmail($varName, $email, $name) { + if (!is_array($email)) { + if (!Validation::email($email)) { + throw new SocketException(__d('cake', 'Invalid email: "%s"', $email)); + } + if ($name === null) { + $name = $email; + } + $this->{$varName}[$email] = $name; + return $this; + } + $list = array(); + foreach ($email as $key => $value) { + if (is_int($key)) { + $key = $value; + } + if (!Validation::email($key)) { + throw new SocketException(__d('cake', 'Invalid email: "%s"', $key)); + } + $list[$key] = $value; + } + $this->{$varName} = array_merge($this->{$varName}, $list); + return $this; + } + +/** + * Set Subject + * + * @param string $subject + * @return mixed + */ + public function subject($subject = null) { + if ($subject === null) { + return $this->_subject; + } + $this->_subject = $this->_encode((string)$subject); + return $this; + } + +/** + * Sets headers for the message + * + * @param array $headers Associative array containing headers to be set. + * @return CakeEmail $this + * @throws SocketException + */ + public function setHeaders($headers) { + if (!is_array($headers)) { + throw new SocketException(__d('cake', '$headers should be an array.')); + } + $this->_headers = $headers; + return $this; + } + +/** + * Add header for the message + * + * @param array $headers + * @return object $this + * @throws SocketException + */ + public function addHeaders($headers) { + if (!is_array($headers)) { + throw new SocketException(__d('cake', '$headers should be an array.')); + } + $this->_headers = array_merge($this->_headers, $headers); + return $this; + } + +/** + * Get list of headers + * + * ### Includes: + * + * - `from` + * - `replyTo` + * - `readReceipt` + * - `returnPath` + * - `to` + * - `cc` + * - `bcc` + * - `subject` + * + * @param array $include + * @return array + */ + public function getHeaders($include = array()) { + if ($include == array_values($include)) { + $include = array_fill_keys($include, true); + } + $defaults = array_fill_keys(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc', 'subject'), false); + $include += $defaults; + + $headers = array(); + $relation = array( + 'from' => 'From', + 'replyTo' => 'Reply-To', + 'readReceipt' => 'Disposition-Notification-To', + 'returnPath' => 'Return-Path' + ); + foreach ($relation as $var => $header) { + if ($include[$var]) { + $var = '_' . $var; + $headers[$header] = current($this->_formatAddress($this->{$var})); + } + } + if ($include['sender']) { + if (key($this->_sender) === key($this->_from)) { + $headers['Sender'] = ''; + } else { + $headers['Sender'] = current($this->_formatAddress($this->_sender)); + } + } + + foreach (array('to', 'cc', 'bcc') as $var) { + if ($include[$var]) { + $classVar = '_' . $var; + $headers[ucfirst($var)] = implode(', ', $this->_formatAddress($this->{$classVar})); + } + } + + $headers += $this->_headers; + if (!isset($headers['X-Mailer'])) { + $headers['X-Mailer'] = self::EMAIL_CLIENT; + } + if (!isset($headers['Date'])) { + $headers['Date'] = date(DATE_RFC2822); + } + if ($this->_messageId !== false) { + if ($this->_messageId === true) { + $headers['Message-ID'] = '<' . String::UUID() . '@' . env('HTTP_HOST') . '>'; + } else { + $headers['Message-ID'] = $this->_messageId; + } + } + + if ($include['subject']) { + $headers['Subject'] = $this->_subject; + } + + $headers['MIME-Version'] = '1.0'; + if (!empty($this->_attachments)) { + $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"'; + $headers[] = 'This part of the E-mail should never be seen. If'; + $headers[] = 'you are reading this, consider upgrading your e-mail'; + $headers[] = 'client to a MIME-compatible client.'; + } elseif ($this->_emailFormat === 'text') { + $headers['Content-Type'] = 'text/plain; charset=' . $this->charset; + } elseif ($this->_emailFormat === 'html') { + $headers['Content-Type'] = 'text/html; charset=' . $this->charset; + } elseif ($this->_emailFormat === 'both') { + $headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->_boundary . '"'; + } + $headers['Content-Transfer-Encoding'] = '7bit'; + + return $headers; + } + +/** + * Format addresses + * + * @param array $address + * @return array + */ + protected function _formatAddress($address) { + $return = array(); + foreach ($address as $email => $alias) { + if ($email === $alias) { + $return[] = $email; + } else { + $return[] = sprintf('%s <%s>', $this->_encode($alias), $email); + } + } + return $return; + } + +/** + * Template and layout + * + * @param mixed $template Template name or null to not use + * @param mixed $layout Layout name or null to not use + * @return mixed + */ + public function template($template = false, $layout = false) { + if ($template === false) { + return array( + 'template' => $this->_template, + 'layout' => $this->_layout + ); + } + $this->_template = $template; + if ($layout !== false) { + $this->_layout = $layout; + } + return $this; + } + +/** + * View class for render + * + * @param string $viewClass + * @return mixed + */ + public function viewRender($viewClass = null) { + if ($viewClass === null) { + return $this->_viewRender; + } + $this->_viewRender = $viewClass; + return $this; + } + +/** + * Variables to be set on render + * + * @param array $viewVars + * @return mixed + */ + public function viewVars($viewVars = null) { + if ($viewVars === null) { + return $this->_viewVars; + } + $this->_viewVars = array_merge($this->_viewVars, (array)$viewVars); + return $this; + } + +/** + * Helpers to be used in render + * + * @param array $helpers + * @return mixed + */ + public function helpers($helpers = null) { + if ($helpers === null) { + return $this->_helpers; + } + $this->_helpers = (array)$helpers; + return $this; + } + +/** + * Email format + * + * @param string $format + * @return mixed + * @throws SocketException + */ + public function emailFormat($format = null) { + if ($format === null) { + return $this->_emailFormat; + } + if (!in_array($format, $this->_emailFormatAvailable)) { + throw new SocketException(__d('cake', 'Format not available.')); + } + $this->_emailFormat = $format; + return $this; + } + +/** + * Transport name + * + * @param string $name + * @return mixed + */ + public function transport($name = null) { + if ($name === null) { + return $this->_transportName; + } + $this->_transportName = (string)$name; + $this->_transportClass = null; + return $this; + } + +/** + * Return the transport class + * + * @return CakeEmail + * @throws SocketException + */ + public function transportClass() { + if ($this->_transportClass) { + return $this->_transportClass; + } + list($plugin, $transportClassname) = pluginSplit($this->_transportName, true); + $transportClassname .= 'Transport'; + App::uses($transportClassname, $plugin . 'Network/Email'); + if (!class_exists($transportClassname)) { + throw new SocketException(__d('cake', 'Class "%s" not found.', $transportClassname)); + } elseif (!method_exists($transportClassname, 'send')) { + throw new SocketException(__d('cake', 'The "%s" do not have send method.', $transportClassname)); + } + + return $this->_transportClass = new $transportClassname(); + } + +/** + * Message-ID + * + * @param mixed $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID + * @return mixed + * @throws SocketException + */ + public function messageId($message = null) { + if ($message === null) { + return $this->_messageId; + } + if (is_bool($message)) { + $this->_messageId = $message; + } else { + if (!preg_match('/^\<.+@.+\>$/', $message)) { + throw new SocketException(__d('cake', 'Invalid format to Message-ID. The text should be something like ""')); + } + $this->_messageId = $message; + } + return $this; + } + +/** + * Attachments + * + * @param mixed $attachments String with the filename or array with filenames + * @return mixed + * @throws SocketException + */ + public function attachments($attachments = null) { + if ($attachments === null) { + return $this->_attachments; + } + $attach = array(); + foreach ((array)$attachments as $name => $fileInfo) { + if (!is_array($fileInfo)) { + $fileInfo = array('file' => $fileInfo); + } + if (!isset($fileInfo['file'])) { + throw new SocketException(__d('cake', 'File not specified.')); + } + $fileInfo['file'] = realpath($fileInfo['file']); + if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) { + throw new SocketException(__d('cake', 'File not found: "%s"', $fileInfo['file'])); + } + if (is_int($name)) { + $name = basename($fileInfo['file']); + } + if (!isset($fileInfo['mimetype'])) { + $fileInfo['mimetype'] = 'application/octet-stream'; + } + $attach[$name] = $fileInfo; + } + $this->_attachments = $attach; + return $this; + } + +/** + * Add attachments + * + * @param mixed $attachments String with the filename or array with filenames + * @return CakeEmail $this + * @throws SocketException + */ + public function addAttachments($attachments) { + $current = $this->_attachments; + $this->attachments($attachments); + $this->_attachments = array_merge($current, $this->_attachments); + return $this; + } + +/** + * Get generated message (used by transport classes) + * + * @param mixed $type Use MESSAGE_* constants or null to return the full message as array + * @return mixed String if have type, array if type is null + */ + public function message($type = null) { + switch ($type) { + case self::MESSAGE_HTML: + return $this->_htmlMessage; + case self::MESSAGE_TEXT: + return $this->_textMessage; + } + return $this->_message; + } + +/** + * Configuration to use when send email + * + * @param mixed $config String with configuration name (from email.php), array with config or null to return current config + * @return mixed + */ + public function config($config = null) { + if ($config === null) { + return $this->_config; + } + if (!is_array($config)) { + $config = (string)$config; + } + + $this->_applyConfig($config); + return $this; + } + +/** + * Send an email using the specified content, template and layout + * + * @return array + * @throws SocketException + */ + public function send($content = null) { + if (empty($this->_from)) { + throw new SocketException(__d('cake', 'From is not specified.')); + } + if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) { + throw new SocketException(__d('cake', 'You need specify one destination on to, cc or bcc.')); + } + + if (is_array($content)) { + $content = implode("\n", $content) . "\n"; + } + + $this->_textMessage = $this->_htmlMessage = ''; + if ($content !== null) { + if ($this->_emailFormat === 'text') { + $this->_textMessage = $content; + } elseif ($this->_emailFormat === 'html') { + $this->_htmlMessage = $content; + } elseif ($this->_emailFormat === 'both') { + $this->_textMessage = $this->_htmlMessage = $content; + } + } + + $this->_createBoundary(); + + $message = $this->_wrap($content); + if (empty($this->_template)) { + $message = $this->_formatMessage($message); + } else { + $message = $this->_render($message); + } + $message[] = ''; + $this->_message = $message; + + if (!empty($this->_attachments)) { + $this->_attachFiles(); + + $this->_message[] = ''; + $this->_message[] = '--' . $this->_boundary . '--'; + $this->_message[] = ''; + } + $contents = $this->transportClass()->send($this); + if (!empty($this->_config['log'])) { + $level = LOG_DEBUG; + if ($this->_config['log'] !== true) { + $level = $this->_config['log']; + } + CakeLog::write($level, PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message']); + } + return $contents; + } + +/** + * Static method to fast create an instance of CakeEmail + * + * @param mixed $to Address to send (see CakeEmail::to()). If null, will try to use 'to' from transport config + * @param mixed $subject String of subject or null to use 'subject' from transport config + * @param mixed $message String with message or array with variables to be used in render + * @param mixed $transportConfig String to use config from EmailConfig or array with configs + * @param boolean $send Send the email or just return the instance pre-configured + * @return CakeEmail Instance of CakeEmail + * @throws SocketException + */ + public static function deliver($to = null, $subject = null, $message = null, $transportConfig = 'fast', $send = true) { + $class = __CLASS__; + $instance = new $class($transportConfig); + if ($to !== null) { + $instance->to($to); + } + if ($subject !== null) { + $instance->subject($subject); + } + if (is_array($message)) { + $instance->viewVars($message); + $message = null; + } elseif ($message === null && array_key_exists('message', $config = $instance->config())) { + $message = $config['message']; + } + + if ($send === true) { + $instance->send($message); + } + + return $instance; + } + +/** + * Apply the config to an instance + * + * @param CakeEmail $obj CakeEmail + * @param array $config + * @return void + */ + protected function _applyConfig($config) { + if (is_string($config)) { + if (!class_exists('EmailConfig') && !config('email')) { + throw new SocketException(__d('cake', '%s not found.', APP . 'Config' . DS . 'email.php')); + } + $configs = new EmailConfig(); + if (!isset($configs->{$config})) { + throw new SocketException(__d('cake', 'Unknown email configuration "%s".', $config)); + } + $config = $configs->{$config}; + } + $this->_config += $config; + $simpleMethods = array( + 'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc', + 'messageId', 'subject', 'viewRender', 'viewVars', 'attachments', + 'transport', 'emailFormat' + ); + foreach ($simpleMethods as $method) { + if (isset($config[$method])) { + $this->$method($config[$method]); + unset($config[$method]); + } + } + if (isset($config['headers'])) { + $this->setHeaders($config['headers']); + unset($config['headers']); + } + if (array_key_exists('template', $config)) { + $layout = false; + if (array_key_exists('layout', $config)) { + $layout = $config['layout']; + unset($config['layout']); + } + $this->template($config['template'], $layout); + unset($config['template']); + } + $this->transportClass()->config($config); + } + +/** + * Reset all EmailComponent internal variables to be able to send out a new email. + * + * @return CakeEmail $this + */ + public function reset() { + $this->_to = array(); + $this->_from = array(); + $this->_sender = array(); + $this->_replyTo = array(); + $this->_readReceipt = array(); + $this->_returnPath = array(); + $this->_cc = array(); + $this->_bcc = array(); + $this->_messageId = true; + $this->_subject = ''; + $this->_headers = array(); + $this->_layout = 'default'; + $this->_template = ''; + $this->_viewRender = 'View'; + $this->_viewVars = array(); + $this->_helpers = array(); + $this->_textMessage = ''; + $this->_htmlMessage = ''; + $this->_message = ''; + $this->_emailFormat = 'text'; + $this->_transportName = 'Mail'; + $this->_transportClass = null; + $this->_attachments = array(); + $this->_config = array(); + return $this; + } + +/** + * Encode the specified string using the current charset + * + * @param string $text String to encode + * @return string Encoded string + */ + protected function _encode($text) { + $internalEncoding = function_exists('mb_internal_encoding'); + if ($internalEncoding) { + $restore = mb_internal_encoding(); + mb_internal_encoding($this->charset); + } + $return = mb_encode_mimeheader($text, $this->charset, 'B'); + if ($internalEncoding) { + mb_internal_encoding($restore); + } + return $return; + } + +/** + * Wrap the message to follow the RFC 2822 - 2.1.1 + * + * @param string $message Message to wrap + * @return array Wrapped message + */ + protected function _wrap($message) { + $message = str_replace(array("\r\n", "\r"), "\n", $message); + $lines = explode("\n", $message); + $formatted = array(); + + foreach ($lines as $line) { + if (empty($line)) { + $formatted[] = ''; + continue; + } + if ($line[0] === '.') { + $line = '.' . $line; + } + if (!preg_match('/\<[a-z]/i', $line)) { + $formatted = array_merge($formatted, explode("\n", wordwrap($line, self::LINE_LENGTH_SHOULD, "\n"))); + continue; + } + + $tagOpen = false; + $tmpLine = $tag = ''; + $tmpLineLength = 0; + for ($i = 0, $count = strlen($line); $i < $count; $i++) { + $char = $line[$i]; + if ($tagOpen) { + $tag .= $char; + if ($char === '>') { + $tagLength = strlen($tag); + if ($tagLength + $tmpLineLength < self::LINE_LENGTH_SHOULD) { + $tmpLine .= $tag; + $tmpLineLength += $tagLength; + } else { + if ($tmpLineLength > 0) { + $formatted[] = trim($tmpLine); + $tmpLine = ''; + $tmpLineLength = 0; + } + if ($tagLength > self::LINE_LENGTH_SHOULD) { + $formatted[] = $tag; + } else { + $tmpLine = $tag; + $tmpLineLength = $tagLength; + } + } + $tag = ''; + $tagOpen = false; + } + continue; + } + if ($char === '<') { + $tagOpen = true; + $tag = '<'; + continue; + } + if ($char === ' ' && $tmpLineLength >= self::LINE_LENGTH_SHOULD) { + $formatted[] = $tmpLine; + $tmpLineLength = 0; + continue; + } + $tmpLine .= $char; + $tmpLineLength++; + if ($tmpLineLength === self::LINE_LENGTH_SHOULD) { + $nextChar = $line[$i + 1]; + if ($nextChar === ' ' || $nextChar === '<') { + $formatted[] = trim($tmpLine); + $tmpLine = ''; + $tmpLineLength = 0; + if ($nextChar === ' ') { + $i++; + } + } else { + $lastSpace = strrpos($tmpLine, ' '); + if ($lastSpace === false) { + continue; + } + $formatted[] = trim(substr($tmpLine, 0, $lastSpace)); + $tmpLine = substr($tmpLine, $lastSpace + 1); + $tmpLineLength = strlen($tmpLine); + } + } + } + if (!empty($tmpLine)) { + $formatted[] = $tmpLine; + } + } + $formatted[] = ''; + return $formatted; + } + +/** + * Create unique boundary identifier + * + * @return void + */ + protected function _createBoundary() { + if (!empty($this->_attachments) || $this->_emailFormat === 'both') { + $this->_boundary = md5(uniqid(time())); + } + } + +/** + * Attach files by adding file contents inside boundaries. + * + * @return void + */ + protected function _attachFiles() { + foreach ($this->_attachments as $filename => $fileInfo) { + $handle = fopen($fileInfo['file'], 'rb'); + $data = fread($handle, filesize($fileInfo['file'])); + $data = chunk_split(base64_encode($data)) ; + fclose($handle); + + $this->_message[] = '--' . $this->_boundary; + $this->_message[] = 'Content-Type: ' . $fileInfo['mimetype']; + $this->_message[] = 'Content-Transfer-Encoding: base64'; + if (empty($fileInfo['contentId'])) { + $this->_message[] = 'Content-Disposition: attachment; filename="' . $filename . '"'; + } else { + $this->_message[] = 'Content-ID: <' . $fileInfo['contentId'] . '>'; + $this->_message[] = 'Content-Disposition: inline; filename="' . $filename . '"'; + } + $this->_message[] = ''; + $this->_message[] = $data; + $this->_message[] = ''; + } + } + +/** + * Format the message by seeing if it has attachments. + * + * @param array $message Message to format + * @return array + */ + protected function _formatMessage($message) { + if (!empty($this->_attachments)) { + $prefix = array('--' . $this->_boundary); + if ($this->_emailFormat === 'text') { + $prefix[] = 'Content-Type: text/plain; charset=' . $this->charset; + } elseif ($this->_emailFormat === 'html') { + $prefix[] = 'Content-Type: text/html; charset=' . $this->charset; + } elseif ($this->_emailFormat === 'both') { + $prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"'; + } + $prefix[] = 'Content-Transfer-Encoding: 7bit'; + $prefix[] = ''; + $message = array_merge($prefix, $message); + } + return $message; + } + +/** + * Render the contents using the current layout and template. + * + * @param string $content Content to render + * @return array Email ready to be sent + */ + protected function _render($content) { + $viewClass = $this->_viewRender; + + if ($viewClass !== 'View') { + list($plugin, $viewClass) = pluginSplit($viewClass, true); + $viewClass .= 'View'; + App::uses($viewClass, $plugin . 'View'); + } + + $View = new $viewClass(null); + $View->viewVars = $this->_viewVars; + $View->helpers = $this->_helpers; + $msg = array(); + + list($templatePlugin, $template) = pluginSplit($this->_template); + list($layoutPlugin, $layout) = pluginSplit($this->_layout); + if ($templatePlugin) { + $View->plugin = $templatePlugin; + } elseif ($layoutPlugin) { + $View->plugin = $layoutPlugin; + } + + $content = implode("\n", $content); + + if ($this->_emailFormat === 'both') { + $originalContent = $content; + if (!empty($this->_attachments)) { + $msg[] = '--' . $this->_boundary; + $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"'; + $msg[] = ''; + } + $msg[] = '--alt-' . $this->_boundary; + $msg[] = 'Content-Type: text/plain; charset=' . $this->charset; + $msg[] = 'Content-Transfer-Encoding: 7bit'; + $msg[] = ''; + + $View->viewPath = $View->layoutPath = 'Emails' . DS . 'text'; + $View->viewVars['content'] = $originalContent; + $this->_textMessage = str_replace(array("\r\n", "\r"), "\n", $View->render($template, $layout)); + $content = explode("\n", $this->_textMessage); + $msg = array_merge($msg, $content); + + $msg[] = ''; + $msg[] = '--alt-' . $this->_boundary; + $msg[] = 'Content-Type: text/html; charset=' . $this->charset; + $msg[] = 'Content-Transfer-Encoding: 7bit'; + $msg[] = ''; + + $View->viewPath = $View->layoutPath = 'Emails' . DS . 'html'; + $View->viewVars['content'] = $originalContent; + $View->hasRendered = false; + $this->_htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->render($template, $layout)); + $content = explode("\n", $this->_htmlMessage); + $msg = array_merge($msg, $content); + + $msg[] = ''; + $msg[] = '--alt-' . $this->_boundary . '--'; + $msg[] = ''; + + return $msg; + } + + if (!empty($this->_attachments)) { + if ($this->_emailFormat === 'html') { + $msg[] = ''; + $msg[] = '--' . $this->_boundary; + $msg[] = 'Content-Type: text/html; charset=' . $this->charset; + $msg[] = 'Content-Transfer-Encoding: 7bit'; + $msg[] = ''; + } else { + $msg[] = '--' . $this->_boundary; + $msg[] = 'Content-Type: text/plain; charset=' . $this->charset; + $msg[] = 'Content-Transfer-Encoding: 7bit'; + $msg[] = ''; + } + } + + $View->viewPath = $View->layoutPath = 'Emails' . DS . $this->_emailFormat; + $View->viewVars['content'] = $content; + $rendered = $View->render($template, $layout); + $content = explode("\n", $rendered); + + if ($this->_emailFormat === 'html') { + $this->_htmlMessage = $rendered; + } else { + $this->_textMessage = $rendered; + } + + return array_merge($msg, $content); + } + +} diff --git a/app/Cake/Network/Email/DebugTransport.php b/app/Cake/Network/Email/DebugTransport.php new file mode 100644 index 00000000..22c0680d --- /dev/null +++ b/app/Cake/Network/Email/DebugTransport.php @@ -0,0 +1,41 @@ +getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc', 'subject')); + $headers = $this->_headersToString($headers); + $message = implode("\r\n", (array)$email->message()); + return array('headers' => $headers, 'message' => $message); + } + +} diff --git a/app/Cake/Network/Email/MailTransport.php b/app/Cake/Network/Email/MailTransport.php new file mode 100644 index 00000000..d90385a0 --- /dev/null +++ b/app/Cake/Network/Email/MailTransport.php @@ -0,0 +1,54 @@ +_config['eol'])) { + $eol = $this->_config['eol']; + } + $headers = $email->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc')); + $to = $headers['To']; + unset($headers['To']); + $headers = $this->_headersToString($headers, $eol); + $message = implode($eol, $email->message()); + if (ini_get('safe_mode') || !isset($this->_config['additionalParameters'])) { + if (!@mail($to, $email->subject(), $message, $headers)) { + throw new SocketException(__d('cake', 'Could not send email.')); + } + } + if(!@mail($to, $email->subject(), $message, $headers, $this->_config['additionalParameters'])) { + throw new SocketException(__d('cake', 'Could not send email.')); + } + return array('headers' => $headers, 'message' => $message); + } + +} diff --git a/app/Cake/Network/Email/SmtpTransport.php b/app/Cake/Network/Email/SmtpTransport.php new file mode 100644 index 00000000..f16cfed3 --- /dev/null +++ b/app/Cake/Network/Email/SmtpTransport.php @@ -0,0 +1,229 @@ +_cakeEmail = $email; + + $this->_connect(); + $this->_auth(); + $this->_sendRcpt(); + $this->_sendData(); + $this->_disconnect(); + + return $this->_content; + } + +/** + * Set the configuration + * + * @param array $config + * @return void + */ + public function config($config = array()) { + $default = array( + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => null, + 'password' => null, + 'client' => null + ); + $this->_config = $config + $default; + } + +/** + * Connect to SMTP Server + * + * @return void + * @throws SocketException + */ + protected function _connect() { + $this->_generateSocket(); + if (!$this->_socket->connect()) { + throw new SocketException(__d('cake', 'Unable to connect in SMTP server.')); + } + $this->_smtpSend(null, '220'); + + if (isset($this->_config['client'])) { + $host = $this->_config['client']; + } elseif ($httpHost = env('HTTP_HOST')) { + list($host) = explode(':', $httpHost); + } else { + $host = 'localhost'; + } + + try { + $this->_smtpSend("EHLO {$host}", '250'); + } catch (SocketException $e) { + try { + $this->_smtpSend("HELO {$host}", '250'); + } catch (SocketException $e2) { + throw new SocketException(__d('cake', 'SMTP server not accepted the connection.')); + } + } + } + +/** + * Send authentication + * + * @return void + * @throws SocketException + */ + protected function _auth() { + if (isset($this->_config['username']) && isset($this->_config['password'])) { + $authRequired = $this->_smtpSend('AUTH LOGIN', '334|503'); + if ($authRequired == '334') { + if (!$this->_smtpSend(base64_encode($this->_config['username']), '334')) { + throw new SocketException(__d('cake', 'SMTP server not accepted the username.')); + } + if (!$this->_smtpSend(base64_encode($this->_config['password']), '235')) { + throw new SocketException(__d('cake', 'SMTP server not accepted the password.')); + } + } elseif ($authRequired != '503') { + throw new SocketException(__d('cake', 'SMTP do not require authentication.')); + } + } + } + +/** + * Send emails + * + * @return void + * @throws SocketException + */ + protected function _sendRcpt() { + $from = $this->_cakeEmail->from(); + $this->_smtpSend('MAIL FROM:<' . key($from) . '>'); + + $to = $this->_cakeEmail->to(); + $cc = $this->_cakeEmail->cc(); + $bcc = $this->_cakeEmail->bcc(); + $emails = array_merge(array_keys($to), array_keys($cc), array_keys($bcc)); + foreach ($emails as $email) { + $this->_smtpSend('RCPT TO:<' . $email . '>'); + } + } + +/** + * Send Data + * + * @return void + * @throws SocketException + */ + protected function _sendData() { + $this->_smtpSend('DATA', '354'); + + $headers = $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc', 'subject')); + $headers = $this->_headersToString($headers); + $message = implode("\r\n", $this->_cakeEmail->message()); + $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n."); + $this->_content = array('headers' => $headers, 'message' => $message); + } + +/** + * Disconnect + * + * @return void + * @throws SocketException + */ + protected function _disconnect() { + $this->_smtpSend('QUIT', false); + $this->_socket->disconnect(); + } + +/** + * Helper method to generate socket + * + * @return void + * @throws SocketException + */ + protected function _generateSocket() { + $this->_socket = new CakeSocket($this->_config); + } + +/** + * Protected method for sending data to SMTP connection + * + * @param string $data data to be sent to SMTP server + * @param mixed $checkCode code to check for in server response, false to skip + * @return void + * @throws SocketException + */ + protected function _smtpSend($data, $checkCode = '250') { + if (!is_null($data)) { + $this->_socket->write($data . "\r\n"); + } + while ($checkCode !== false) { + $response = ''; + $startTime = time(); + while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) { + $response .= $this->_socket->read(); + } + if (substr($response, -2) !== "\r\n") { + throw new SocketException(__d('cake', 'SMTP timeout.')); + } + $response = end(explode("\r\n", rtrim($response, "\r\n"))); + + if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) { + if ($code[2] === '-') { + continue; + } + return $code[1]; + } + throw new SocketException(__d('cake', 'SMTP Error: %s', $response)); + } + } + +} diff --git a/app/Cake/Network/Http/BasicAuthentication.php b/app/Cake/Network/Http/BasicAuthentication.php new file mode 100644 index 00000000..051e4c4c --- /dev/null +++ b/app/Cake/Network/Http/BasicAuthentication.php @@ -0,0 +1,66 @@ +request['header']['Authorization'] = self::_generateHeader($authInfo['user'], $authInfo['pass']); + } + } + +/** + * Proxy Authentication + * + * @param HttpSocket $http + * @param array $proxyInfo + * @return void + * @see http://www.ietf.org/rfc/rfc2617.txt + */ + public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) { + if (isset($proxyInfo['user'], $proxyInfo['pass'])) { + $http->request['header']['Proxy-Authorization'] = self::_generateHeader($proxyInfo['user'], $proxyInfo['pass']); + } + } + +/** + * Generate basic [proxy] authentication header + * + * @param string $user + * @param string $pass + * @return string + */ + protected static function _generateHeader($user, $pass) { + return 'Basic ' . base64_encode($user . ':' . $pass); + } + +} diff --git a/app/Cake/Network/Http/DigestAuthentication.php b/app/Cake/Network/Http/DigestAuthentication.php new file mode 100644 index 00000000..9680e642 --- /dev/null +++ b/app/Cake/Network/Http/DigestAuthentication.php @@ -0,0 +1,104 @@ +request['header']['Authorization'] = self::_generateHeader($http, $authInfo); + } + } + +/** + * Retrive information about the authetication + * + * @param HttpSocket $http + * @param array $authInfo + * @return boolean + */ + protected static function _getServerInformation(HttpSocket $http, &$authInfo) { + $originalRequest = $http->request; + $http->configAuth(false); + $http->request($http->request); + $http->request = $originalRequest; + $http->configAuth('Digest', $authInfo); + + if (empty($http->response['header']['WWW-Authenticate'])) { + return false; + } + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['WWW-Authenticate'], $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $authInfo[$match[1]] = $match[2]; + } + if (!empty($authInfo['qop']) && empty($authInfo['nc'])) { + $authInfo['nc'] = 1; + } + return true; + } + +/** + * Generate the header Authorization + * + * @param HttpSocket $http + * @param array $authInfo + * @return string + */ + protected static function _generateHeader(HttpSocket $http, &$authInfo) { + $a1 = md5($authInfo['user'] . ':' . $authInfo['realm'] . ':' . $authInfo['pass']); + $a2 = md5($http->request['method'] . ':' . $http->request['uri']['path']); + + if (empty($authInfo['qop'])) { + $response = md5($a1 . ':' . $authInfo['nonce'] . ':' . $a2); + } else { + $authInfo['cnonce'] = uniqid(); + $nc = sprintf('%08x', $authInfo['nc']++); + $response = md5($a1 . ':' . $authInfo['nonce'] . ':' . $nc . ':' . $authInfo['cnonce'] . ':auth:' . $a2); + } + + $authHeader = 'Digest '; + $authHeader .= 'username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $authInfo['user']) . '", '; + $authHeader .= 'realm="' . $authInfo['realm'] . '", '; + $authHeader .= 'nonce="' . $authInfo['nonce'] . '", '; + $authHeader .= 'uri="' . $http->request['uri']['path'] . '", '; + $authHeader .= 'response="' . $response . '"'; + if (!empty($authInfo['opaque'])) { + $authHeader .= ', opaque="' . $authInfo['opaque'] . '"'; + } + if (!empty($authInfo['qop'])) { + $authHeader .= ', qop="auth", nc=' . $nc . ', cnonce="' . $authInfo['cnonce'] . '"'; + } + return $authHeader; + } +} diff --git a/app/Cake/Network/Http/HttpResponse.php b/app/Cake/Network/Http/HttpResponse.php new file mode 100644 index 00000000..976344be --- /dev/null +++ b/app/Cake/Network/Http/HttpResponse.php @@ -0,0 +1,438 @@ +parseResponse($message); + } + } + +/** + * Body content + * + * @return string + */ + public function body() { + return (string)$this->body; + } + +/** + * Get header in case insensitive + * + * @param string $name Header name + * @param array $headers + * @return mixed String if header exists or null + */ + public function getHeader($name, $headers = null) { + if (!is_array($headers)) { + $headers =& $this->headers; + } + if (isset($headers[$name])) { + return $headers[$name]; + } + foreach ($headers as $key => $value) { + if (strcasecmp($key, $name) == 0) { + return $value; + } + } + return null; + } + +/** + * If return is 200 (OK) + * + * @return boolean + */ + public function isOk() { + return $this->code == 200; + } + +/** + * Parses the given message and breaks it down in parts. + * + * @param string $message Message to parse + * @return void + * @throws SocketException + */ + public function parseResponse($message) { + if (!is_string($message)) { + throw new SocketException(__d('cake_dev', 'Invalid response.')); + } + + if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) { + throw new SocketException(__d('cake_dev', 'Invalid HTTP response.')); + } + + list(, $statusLine, $header) = $match; + $this->raw = $message; + $this->body = (string)substr($message, strlen($match[0])); + + if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $statusLine, $match)) { + $this->httpVersion = $match[1]; + $this->code = $match[2]; + $this->reasonPhrase = $match[3]; + } + + $this->headers = $this->_parseHeader($header); + $transferEncoding = $this->getHeader('Transfer-Encoding'); + $decoded = $this->_decodeBody($this->body, $transferEncoding); + $this->body = $decoded['body']; + + if (!empty($decoded['header'])) { + $this->headers = $this->_parseHeader($this->_buildHeader($this->headers) . $this->_buildHeader($decoded['header'])); + } + + if (!empty($this->headers)) { + $this->cookies = $this->parseCookies($this->headers); + } + } + +/** + * Generic function to decode a $body with a given $encoding. Returns either an array with the keys + * 'body' and 'header' or false on failure. + * + * @param string $body A string continaing the body to decode. + * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding. + * @return mixed Array of response headers and body or false. + */ + protected function _decodeBody($body, $encoding = 'chunked') { + if (!is_string($body)) { + return false; + } + if (empty($encoding)) { + return array('body' => $body, 'header' => false); + } + $decodeMethod = '_decode' . Inflector::camelize(str_replace('-', '_', $encoding)) . 'Body'; + + if (!is_callable(array(&$this, $decodeMethod))) { + return array('body' => $body, 'header' => false); + } + return $this->{$decodeMethod}($body); + } + +/** + * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as + * a result. + * + * @param string $body A string continaing the chunked body to decode. + * @return mixed Array of response headers and body or false. + * @throws SocketException + */ + protected function _decodeChunkedBody($body) { + if (!is_string($body)) { + return false; + } + + $decodedBody = null; + $chunkLength = null; + + while ($chunkLength !== 0) { + if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { + throw new SocketException(__d('cake_dev', 'HttpSocket::_decodeChunkedBody - Could not parse malformed chunk.')); + } + + $chunkSize = 0; + $hexLength = 0; + $chunkExtensionName = ''; + $chunkExtensionValue = ''; + if (isset($match[0])) { + $chunkSize = $match[0]; + } + if (isset($match[1])) { + $hexLength = $match[1]; + } + if (isset($match[2])) { + $chunkExtensionName = $match[2]; + } + if (isset($match[3])) { + $chunkExtensionValue = $match[3]; + } + + $body = substr($body, strlen($chunkSize)); + $chunkLength = hexdec($hexLength); + $chunk = substr($body, 0, $chunkLength); + if (!empty($chunkExtensionName)) { + /** + * @todo See if there are popular chunk extensions we should implement + */ + } + $decodedBody .= $chunk; + if ($chunkLength !== 0) { + $body = substr($body, $chunkLength + strlen("\r\n")); + } + } + + $entityHeader = false; + if (!empty($body)) { + $entityHeader = $this->_parseHeader($body); + } + return array('body' => $decodedBody, 'header' => $entityHeader); + } + +/** + * Parses an array based header. + * + * @param array $header Header as an indexed array (field => value) + * @return array Parsed header + */ + protected function _parseHeader($header) { + if (is_array($header)) { + return $header; + } elseif (!is_string($header)) { + return false; + } + + preg_match_all("/(.+):(.+)(?:(?_unescapeToken($field); + + if (!isset($header[$field])) { + $header[$field] = $value; + } else { + $header[$field] = array_merge((array)$header[$field], (array)$value); + } + } + return $header; + } + +/** + * Parses cookies in response headers. + * + * @param array $header Header array containing one ore more 'Set-Cookie' headers. + * @return mixed Either false on no cookies, or an array of cookies recieved. + * @todo Make this 100% RFC 2965 confirm + */ + public function parseCookies($header) { + $cookieHeader = $this->getHeader('Set-Cookie', $header); + if (!$cookieHeader) { + return false; + } + + $cookies = array(); + foreach ((array)$cookieHeader as $cookie) { + if (strpos($cookie, '";"') !== false) { + $cookie = str_replace('";"', "{__cookie_replace__}", $cookie); + $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie)); + } else { + $parts = preg_split('/\;[ \t]*/', $cookie); + } + + list($name, $value) = explode('=', array_shift($parts), 2); + $cookies[$name] = compact('value'); + + foreach ($parts as $part) { + if (strpos($part, '=') !== false) { + list($key, $value) = explode('=', $part); + } else { + $key = $part; + $value = true; + } + + $key = strtolower($key); + if (!isset($cookies[$name][$key])) { + $cookies[$name][$key] = $value; + } + } + } + return $cookies; + } + +/** + * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs) + * + * @param string $token Token to unescape + * @param array $chars + * @return string Unescaped token + * @todo Test $chars parameter + */ + protected function _unescapeToken($token, $chars = null) { + $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/'; + $token = preg_replace($regex, '\\1', $token); + return $token; + } + +/** + * Gets escape chars according to RFC 2616 (HTTP 1.1 specs). + * + * @param boolean $hex true to get them as HEX values, false otherwise + * @param array $chars + * @return array Escape chars + * @todo Test $chars parameter + */ + protected function _tokenEscapeChars($hex = true, $chars = null) { + if (!empty($chars)) { + $escape = $chars; + } else { + $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " "); + for ($i = 0; $i <= 31; $i++) { + $escape[] = chr($i); + } + $escape[] = chr(127); + } + + if ($hex == false) { + return $escape; + } + foreach ($escape as $key => $char) { + $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); + } + return $escape; + } + +/** + * ArrayAccess - Offset Exists + * + * @param mixed $offset + * @return boolean + */ + public function offsetExists($offset) { + return in_array($offset, array('raw', 'status', 'header', 'body', 'cookies')); + } + +/** + * ArrayAccess - Offset Get + * + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) { + switch ($offset) { + case 'raw': + $firstLineLength = strpos($this->raw, "\r\n") + 2; + if ($this->raw[$firstLineLength] === "\r") { + $header = null; + } else { + $header = substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength) . "\r\n"; + } + return array( + 'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase . "\r\n", + 'header' => $header, + 'body' => $this->body, + 'response' => $this->raw + ); + case 'status': + return array( + 'http-version' => $this->httpVersion, + 'code' => $this->code, + 'reason-phrase' => $this->reasonPhrase + ); + case 'header': + return $this->headers; + case 'body': + return $this->body; + case 'cookies': + return $this->cookies; + } + return null; + } + +/** + * ArrayAccess - 0ffset Set + * + * @param mixed $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset, $value) { + return; + } + +/** + * ArrayAccess - Offset Unset + * + * @param mixed $offset + * @return void + */ + public function offsetUnset($offset) { + return; + } + +/** + * Instance as string + * + * @return string + */ + public function __toString() { + return $this->body(); + } + +} diff --git a/app/Cake/Network/Http/HttpSocket.php b/app/Cake/Network/Http/HttpSocket.php new file mode 100644 index 00000000..c1d3056b --- /dev/null +++ b/app/Cake/Network/Http/HttpSocket.php @@ -0,0 +1,947 @@ + 'GET', + 'uri' => array( + 'scheme' => 'http', + 'host' => null, + 'port' => 80, + 'user' => null, + 'pass' => null, + 'path' => null, + 'query' => null, + 'fragment' => null + ), + 'version' => '1.1', + 'body' => '', + 'line' => null, + 'header' => array( + 'Connection' => 'close', + 'User-Agent' => 'CakePHP' + ), + 'raw' => null, + 'cookies' => array() + ); + +/** + * Contain information about the last response (read only) + * + * @var array + */ + public $response = null; + +/** + * Response classname + * + * @var string + */ + public $responseClass = 'HttpResponse'; + +/** + * Configuration settings for the HttpSocket and the requests + * + * @var array + */ + public $config = array( + 'persistent' => false, + 'host' => 'localhost', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30, + 'request' => array( + 'uri' => array( + 'scheme' => 'http', + 'host' => 'localhost', + 'port' => 80 + ), + 'cookies' => array() + ) + ); + +/** + * Authentication settings + * + * @var array + */ + protected $_auth = array(); + +/** + * Proxy settings + * + * @var array + */ + protected $_proxy = array(); + +/** + * Resource to receive the content of request + * + * @var mixed + */ + protected $_contentResource = null; + +/** + * Build an HTTP Socket using the specified configuration. + * + * You can use a url string to set the url and use default configurations for + * all other options: + * + * `$http = new HttpSocket('http://cakephp.org/');` + * + * Or use an array to configure multiple options: + * + * {{{ + * $http = new HttpSocket(array( + * 'host' => 'cakephp.org', + * 'timeout' => 20 + * )); + * }}} + * + * See HttpSocket::$config for options that can be used. + * + * @param mixed $config Configuration information, either a string url or an array of options. + */ + public function __construct($config = array()) { + if (is_string($config)) { + $this->_configUri($config); + } elseif (is_array($config)) { + if (isset($config['request']['uri']) && is_string($config['request']['uri'])) { + $this->_configUri($config['request']['uri']); + unset($config['request']['uri']); + } + $this->config = Set::merge($this->config, $config); + } + parent::__construct($this->config); + } + +/** + * Set authentication settings + * + * @param string $method Authentication method (ie. Basic, Digest). If empty, disable authentication + * @param mixed $user Username for authentication. Can be an array with settings to authentication class + * @param string $pass Password for authentication + * @return void + */ + public function configAuth($method, $user = null, $pass = null) { + if (empty($method)) { + $this->_auth = array(); + return; + } + if (is_array($user)) { + $this->_auth = array($method => $user); + return; + } + $this->_auth = array($method => compact('user', 'pass')); + } + +/** + * Set proxy settings + * + * @param mixed $host Proxy host. Can be an array with settings to authentication class + * @param integer $port Port. Default 3128. + * @param string $method Proxy method (ie, Basic, Digest). If empty, disable proxy authentication + * @param string $user Username if your proxy need authentication + * @param string $pass Password to proxy authentication + * @return void + */ + public function configProxy($host, $port = 3128, $method = null, $user = null, $pass = null) { + if (empty($host)) { + $this->_proxy = array(); + return; + } + if (is_array($host)) { + $this->_proxy = $host + array('host' => null); + return; + } + $this->_proxy = compact('host', 'port', 'method', 'user', 'pass'); + } + +/** + * Set the resource to receive the request content. This resource must support fwrite. + * + * @param mixed $resource Resource or false to disable the resource use + * @return void + * @throws SocketException + */ + public function setContentResource($resource) { + if ($resource === false) { + $this->_contentResource = null; + return; + } + if (!is_resource($resource)) { + throw new SocketException(__d('cake_dev', 'Invalid resource.')); + } + $this->_contentResource = $resource; + } + +/** + * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this + * method and provide a more granular interface. + * + * @param mixed $request Either an URI string, or an array defining host/uri + * @return mixed false on error, HttpResponse on success + * @throws SocketException + */ + public function request($request = array()) { + $this->reset(false); + + if (is_string($request)) { + $request = array('uri' => $request); + } elseif (!is_array($request)) { + return false; + } + + if (!isset($request['uri'])) { + $request['uri'] = null; + } + $uri = $this->_parseUri($request['uri']); + if (!isset($uri['host'])) { + $host = $this->config['host']; + } + if (isset($request['host'])) { + $host = $request['host']; + unset($request['host']); + } + $request['uri'] = $this->url($request['uri']); + $request['uri'] = $this->_parseUri($request['uri'], true); + $this->request = Set::merge($this->request, array_diff_key($this->config['request'], array('cookies' => true)), $request); + + $this->_configUri($this->request['uri']); + + $Host = $this->request['uri']['host']; + if (!empty($this->config['request']['cookies'][$Host])) { + if (!isset($this->request['cookies'])) { + $this->request['cookies'] = array(); + } + if (!isset($request['cookies'])) { + $request['cookies'] = array(); + } + $this->request['cookies'] = array_merge($this->request['cookies'], $this->config['request']['cookies'][$Host], $request['cookies']); + } + + if (isset($host)) { + $this->config['host'] = $host; + } + $this->_setProxy(); + $this->request['proxy'] = $this->_proxy; + + $cookies = null; + + if (is_array($this->request['header'])) { + if (!empty($this->request['cookies'])) { + $cookies = $this->buildCookies($this->request['cookies']); + } + $schema = ''; + $port = 0; + if (isset($this->request['uri']['schema'])) { + $schema = $this->request['uri']['schema']; + } + if (isset($this->request['uri']['port'])) { + $port = $this->request['uri']['port']; + } + if ( + ($schema === 'http' && $port != 80) || + ($schema === 'https' && $port != 443) || + ($port != 80 && $port != 443) + ) { + $Host .= ':' . $port; + } + $this->request['header'] = array_merge(compact('Host'), $this->request['header']); + } + + if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) { + $this->configAuth('Basic', $this->request['uri']['user'], $this->request['uri']['pass']); + } + $this->_setAuth(); + $this->request['auth'] = $this->_auth; + + if (is_array($this->request['body'])) { + $this->request['body'] = $this->_httpSerialize($this->request['body']); + } + + if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) { + $this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) { + $this->request['header']['Content-Length'] = strlen($this->request['body']); + } + + $connectionType = null; + if (isset($this->request['header']['Connection'])) { + $connectionType = $this->request['header']['Connection']; + } + $this->request['header'] = $this->_buildHeader($this->request['header']) . $cookies; + + if (empty($this->request['line'])) { + $this->request['line'] = $this->_buildRequestLine($this->request); + } + + if ($this->quirksMode === false && $this->request['line'] === false) { + return false; + } + + $this->request['raw'] = ''; + if ($this->request['line'] !== false) { + $this->request['raw'] = $this->request['line']; + } + + if ($this->request['header'] !== false) { + $this->request['raw'] .= $this->request['header']; + } + + $this->request['raw'] .= "\r\n"; + $this->request['raw'] .= $this->request['body']; + $this->write($this->request['raw']); + + $response = null; + $inHeader = true; + while ($data = $this->read()) { + if ($this->_contentResource) { + if ($inHeader) { + $response .= $data; + $pos = strpos($response, "\r\n\r\n"); + if ($pos !== false) { + $pos += 4; + $data = substr($response, $pos); + fwrite($this->_contentResource, $data); + + $response = substr($response, 0, $pos); + $inHeader = false; + } + } else { + fwrite($this->_contentResource, $data); + fflush($this->_contentResource); + } + } else { + $response .= $data; + } + } + + if ($connectionType === 'close') { + $this->disconnect(); + } + + list($plugin, $responseClass) = pluginSplit($this->responseClass, true); + App::uses($this->responseClass, $plugin . 'Network/Http'); + if (!class_exists($responseClass)) { + throw new SocketException(__d('cake_dev', 'Class %s not found.', $this->responseClass)); + } + $responseClass = $this->responseClass; + $this->response = new $responseClass($response); + if (!empty($this->response->cookies)) { + if (!isset($this->config['request']['cookies'][$Host])) { + $this->config['request']['cookies'][$Host] = array(); + } + $this->config['request']['cookies'][$Host] = array_merge($this->config['request']['cookies'][$Host], $this->response->cookies); + } + + return $this->response; + } + +/** + * Issues a GET request to the specified URI, query, and request. + * + * Using a string uri and an array of query string parameters: + * + * `$response = $http->get('http://google.com/search', array('q' => 'cakephp', 'client' => 'safari'));` + * + * Would do a GET request to `http://google.com/search?q=cakephp&client=safari` + * + * You could express the same thing using a uri array and query string parameters: + * + * {{{ + * $response = $http->get( + * array('host' => 'google.com', 'path' => '/search'), + * array('q' => 'cakephp', 'client' => 'safari') + * ); + * }}} + * + * @param mixed $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri() + * @param array $query Querystring parameters to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request, either false on failure or the response to the request. + */ + public function get($uri = null, $query = array(), $request = array()) { + if (!empty($query)) { + $uri = $this->_parseUri($uri, $this->config['request']['uri']); + if (isset($uri['query'])) { + $uri['query'] = array_merge($uri['query'], $query); + } else { + $uri['query'] = $query; + } + $uri = $this->_buildUri($uri); + } + + $request = Set::merge(array('method' => 'GET', 'uri' => $uri), $request); + return $this->request($request); + } + +/** + * Issues a POST request to the specified URI, query, and request. + * + * `post()` can be used to post simple data arrays to a url: + * + * {{{ + * $response = $http->post('http://example.com', array( + * 'username' => 'batman', + * 'password' => 'bruce_w4yne' + * )); + * }}} + * + * @param mixed $uri URI to request. See HttpSocket::_parseUri() + * @param array $data Array of POST data keys and values. + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request, either false on failure or the response to the request. + */ + public function post($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } + +/** + * Issues a PUT request to the specified URI, query, and request. + * + * @param mixed $uri URI to request, See HttpSocket::_parseUri() + * @param array $data Array of PUT data keys and values. + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + */ + public function put($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } + +/** + * Issues a DELETE request to the specified URI, query, and request. + * + * @param mixed $uri URI to request (see {@link _parseUri()}) + * @param array $data Query to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + */ + public function delete($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } + +/** + * Normalizes urls into a $uriTemplate. If no template is provided + * a default one will be used. Will generate the url using the + * current config information. + * + * ### Usage: + * + * After configuring part of the request parameters, you can use url() to generate + * urls. + * + * {{{ + * $http->configUri('http://www.cakephp.org'); + * $url = $http->url('/search?q=bar'); + * }}} + * + * Would return `http://www.cakephp.org/search?q=bar` + * + * url() can also be used with custom templates: + * + * `$url = $http->url('http://www.cakephp/search?q=socket', '/%path?%query');` + * + * Would return `/search?q=socket`. + * + * @param mixed $url Either a string or array of url options to create a url with. + * @param string $uriTemplate A template string to use for url formatting. + * @return mixed Either false on failure or a string containing the composed url. + */ + public function url($url = null, $uriTemplate = null) { + if (is_null($url)) { + $url = '/'; + } + if (is_string($url)) { + if ($url{0} == '/') { + $url = $this->config['request']['uri']['host'] . ':' . $this->config['request']['uri']['port'] . $url; + } + if (!preg_match('/^.+:\/\/|\*|^\//', $url)) { + $url = $this->config['request']['uri']['scheme'] . '://' . $url; + } + } elseif (!is_array($url) && !empty($url)) { + return false; + } + + $base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443))); + $url = $this->_parseUri($url, $base); + + if (empty($url)) { + $url = $this->config['request']['uri']; + } + + if (!empty($uriTemplate)) { + return $this->_buildUri($url, $uriTemplate); + } + return $this->_buildUri($url); + } + +/** + * Set authentication in request + * + * @return void + * @throws SocketException + */ + protected function _setAuth() { + if (empty($this->_auth)) { + return; + } + $method = key($this->_auth); + list($plugin, $authClass) = pluginSplit($method, true); + $authClass = Inflector::camelize($authClass) . 'Authentication'; + App::uses($authClass, $plugin . 'Network/Http'); + + if (!class_exists($authClass)) { + throw new SocketException(__d('cake_dev', 'Unknown authentication method.')); + } + if (!method_exists($authClass, 'authentication')) { + throw new SocketException(sprintf(__d('cake_dev', 'The %s do not support authentication.'), $authClass)); + } + call_user_func_array("$authClass::authentication", array($this, &$this->_auth[$method])); + } + +/** + * Set the proxy configuration and authentication + * + * @return void + * @throws SocketException + */ + protected function _setProxy() { + if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) { + return; + } + $this->config['host'] = $this->_proxy['host']; + $this->config['port'] = $this->_proxy['port']; + + if (empty($this->_proxy['method']) || !isset($this->_proxy['user'], $this->_proxy['pass'])) { + return; + } + list($plugin, $authClass) = pluginSplit($this->_proxy['method'], true); + $authClass = Inflector::camelize($authClass) . 'Authentication'; + App::uses($authClass, $plugin. 'Network/Http'); + + if (!class_exists($authClass)) { + throw new SocketException(__d('cake_dev', 'Unknown authentication method for proxy.')); + } + if (!method_exists($authClass, 'proxyAuthentication')) { + throw new SocketException(sprintf(__d('cake_dev', 'The %s do not support proxy authentication.'), $authClass)); + } + call_user_func_array("$authClass::proxyAuthentication", array($this, &$this->_proxy)); + } + +/** + * Parses and sets the specified URI into current request configuration. + * + * @param mixed $uri URI, See HttpSocket::_parseUri() + * @return boolean If uri has merged in config + */ + protected function _configUri($uri = null) { + if (empty($uri)) { + return false; + } + + if (is_array($uri)) { + $uri = $this->_parseUri($uri); + } else { + $uri = $this->_parseUri($uri, true); + } + + if (!isset($uri['host'])) { + return false; + } + $config = array( + 'request' => array( + 'uri' => array_intersect_key($uri, $this->config['request']['uri']) + ) + ); + $this->config = Set::merge($this->config, $config); + $this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config)); + return true; + } + +/** + * Takes a $uri array and turns it into a fully qualified URL string + * + * @param mixed $uri Either A $uri array, or a request string. Will use $this->config if left empty. + * @param string $uriTemplate The Uri template/format to use. + * @return mixed A fully qualified URL formated according to $uriTemplate, or false on failure + */ + protected function _buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') { + if (is_string($uri)) { + $uri = array('host' => $uri); + } + $uri = $this->_parseUri($uri, true); + + if (!is_array($uri) || empty($uri)) { + return false; + } + + $uri['path'] = preg_replace('/^\//', null, $uri['path']); + $uri['query'] = $this->_httpSerialize($uri['query']); + $stripIfEmpty = array( + 'query' => '?%query', + 'fragment' => '#%fragment', + 'user' => '%user:%pass@', + 'host' => '%host:%port/' + ); + + foreach ($stripIfEmpty as $key => $strip) { + if (empty($uri[$key])) { + $uriTemplate = str_replace($strip, null, $uriTemplate); + } + } + + $defaultPorts = array('http' => 80, 'https' => 443); + if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) { + $uriTemplate = str_replace(':%port', null, $uriTemplate); + } + foreach ($uri as $property => $value) { + $uriTemplate = str_replace('%' . $property, $value, $uriTemplate); + } + + if ($uriTemplate === '/*') { + $uriTemplate = '*'; + } + return $uriTemplate; + } + +/** + * Parses the given URI and breaks it down into pieces as an indexed array with elements + * such as 'scheme', 'port', 'query'. + * + * @param string $uri URI to parse + * @param mixed $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc. + * @return array Parsed URI + */ + protected function _parseUri($uri = null, $base = array()) { + $uriBase = array( + 'scheme' => array('http', 'https'), + 'host' => null, + 'port' => array(80, 443), + 'user' => null, + 'pass' => null, + 'path' => '/', + 'query' => null, + 'fragment' => null + ); + + if (is_string($uri)) { + $uri = parse_url($uri); + } + if (!is_array($uri) || empty($uri)) { + return false; + } + if ($base === true) { + $base = $uriBase; + } + + if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) { + if (isset($uri['scheme']) && !isset($uri['port'])) { + $base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])]; + } elseif (isset($uri['port']) && !isset($uri['scheme'])) { + $base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])]; + } + } + + if (is_array($base) && !empty($base)) { + $uri = array_merge($base, $uri); + } + + if (isset($uri['scheme']) && is_array($uri['scheme'])) { + $uri['scheme'] = array_shift($uri['scheme']); + } + if (isset($uri['port']) && is_array($uri['port'])) { + $uri['port'] = array_shift($uri['port']); + } + + if (array_key_exists('query', $uri)) { + $uri['query'] = $this->_parseQuery($uri['query']); + } + + if (!array_intersect_key($uriBase, $uri)) { + return false; + } + return $uri; + } + +/** + * This function can be thought of as a reverse to PHP5's http_build_query(). It takes a given query string and turns it into an array and + * supports nesting by using the php bracket syntax. So this menas you can parse queries like: + * + * - ?key[subKey]=value + * - ?key[]=value1&key[]=value2 + * + * A leading '?' mark in $query is optional and does not effect the outcome of this function. + * For the complete capabilities of this implementation take a look at HttpSocketTest::testparseQuery() + * + * @param mixed $query A query string to parse into an array or an array to return directly "as is" + * @return array The $query parsed into a possibly multi-level array. If an empty $query is + * given, an empty array is returned. + */ + protected function _parseQuery($query) { + if (is_array($query)) { + return $query; + } + $parsedQuery = array(); + + if (is_string($query) && !empty($query)) { + $query = preg_replace('/^\?/', '', $query); + $items = explode('&', $query); + + foreach ($items as $item) { + if (strpos($item, '=') !== false) { + list($key, $value) = explode('=', $item, 2); + } else { + $key = $item; + $value = null; + } + + $key = urldecode($key); + $value = urldecode($value); + + if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) { + $subKeys = $matches[1]; + $rootKey = substr($key, 0, strpos($key, '[')); + if (!empty($rootKey)) { + array_unshift($subKeys, $rootKey); + } + $queryNode =& $parsedQuery; + + foreach ($subKeys as $subKey) { + if (!is_array($queryNode)) { + $queryNode = array(); + } + + if ($subKey === '') { + $queryNode[] = array(); + end($queryNode); + $subKey = key($queryNode); + } + $queryNode =& $queryNode[$subKey]; + } + $queryNode = $value; + } else { + $parsedQuery[$key] = $value; + } + } + } + return $parsedQuery; + } + +/** + * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs. + * + * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET. + * @param string $versionToken The version token to use, defaults to HTTP/1.1 + * @return string Request line + * @throws SocketException + */ + protected function _buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { + $asteriskMethods = array('OPTIONS'); + + if (is_string($request)) { + $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match); + if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) { + throw new SocketException(__d('cake_dev', 'HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.')); + } + return $request; + } elseif (!is_array($request)) { + return false; + } elseif (!array_key_exists('uri', $request)) { + return false; + } + + $request['uri'] = $this->_parseUri($request['uri']); + $request = array_merge(array('method' => 'GET'), $request); + if (!empty($this->_proxy['host'])) { + $request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query'); + } else { + $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query'); + } + + if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { + throw new SocketException(__d('cake_dev', 'HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', implode(',', $asteriskMethods))); + } + return $request['method'] . ' ' . $request['uri'] . ' ' . $versionToken . "\r\n"; + } + +/** + * Serializes an array for transport. + * + * @param array $data Data to serialize + * @return string Serialized variable + */ + protected function _httpSerialize($data = array()) { + if (is_string($data)) { + return $data; + } + if (empty($data) || !is_array($data)) { + return false; + } + return substr(Router::queryString($data), 1); + } + +/** + * Builds the header. + * + * @param array $header Header to build + * @param string $mode + * @return string Header built from array + */ + protected function _buildHeader($header, $mode = 'standard') { + if (is_string($header)) { + return $header; + } elseif (!is_array($header)) { + return false; + } + + $fieldsInHeader = array(); + foreach ($header as $key => $value) { + $lowKey = strtolower($key); + if (array_key_exists($lowKey, $fieldsInHeader)) { + $header[$fieldsInHeader[$lowKey]] = $value; + unset($header[$key]); + } else { + $fieldsInHeader[$lowKey] = $key; + } + } + + $returnHeader = ''; + foreach ($header as $field => $contents) { + if (is_array($contents) && $mode == 'standard') { + $contents = implode(',', $contents); + } + foreach ((array)$contents as $content) { + $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content); + $field = $this->_escapeToken($field); + + $returnHeader .= $field . ': ' . $contents . "\r\n"; + } + } + return $returnHeader; + } + +/** + * Builds cookie headers for a request. + * + * @param array $cookies Array of cookies to send with the request. + * @return string Cookie header string to be sent with the request. + * @todo Refactor token escape mechanism to be configurable + */ + public function buildCookies($cookies) { + $header = array(); + foreach ($cookies as $name => $cookie) { + $header[] = $name . '=' . $this->_escapeToken($cookie['value'], array(';')); + } + return $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic'); + } + +/** + * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs) + * + * @param string $token Token to escape + * @param array $chars + * @return string Escaped token + * @todo Test $chars parameter + */ + protected function _escapeToken($token, $chars = null) { + $regex = '/([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])/'; + $token = preg_replace($regex, '"\\1"', $token); + return $token; + } + +/** + * Gets escape chars according to RFC 2616 (HTTP 1.1 specs). + * + * @param boolean $hex true to get them as HEX values, false otherwise + * @param array $chars + * @return array Escape chars + * @todo Test $chars parameter + */ + protected function _tokenEscapeChars($hex = true, $chars = null) { + if (!empty($chars)) { + $escape = $chars; + } else { + $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " "); + for ($i = 0; $i <= 31; $i++) { + $escape[] = chr($i); + } + $escape[] = chr(127); + } + + if ($hex == false) { + return $escape; + } + foreach ($escape as $key => $char) { + $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); + } + return $escape; + } + +/** + * Resets the state of this HttpSocket instance to it's initial state (before Object::__construct got executed) or does + * the same thing partially for the request and the response property only. + * + * @param boolean $full If set to false only HttpSocket::response and HttpSocket::request are reseted + * @return boolean True on success + */ + public function reset($full = true) { + static $initalState = array(); + if (empty($initalState)) { + $initalState = get_class_vars(__CLASS__); + } + if (!$full) { + $this->request = $initalState['request']; + $this->response = $initalState['response']; + return true; + } + parent::reset($initalState); + return true; + } +} diff --git a/app/Cake/Routing/Dispatcher.php b/app/Cake/Routing/Dispatcher.php new file mode 100644 index 00000000..f526a2f9 --- /dev/null +++ b/app/Cake/Routing/Dispatcher.php @@ -0,0 +1,324 @@ +asset($request->url, $response) || $this->cached($request->here)) { + return; + } + + $request = $this->parseParams($request, $additionalParams); + Router::setRequestInfo($request); + $controller = $this->_getController($request, $response); + + if (!($controller instanceof Controller)) { + throw new MissingControllerException(array( + 'controller' => Inflector::camelize($request->params['controller']) . 'Controller' + )); + } + + return $this->_invoke($controller, $request, $response); + } + +/** + * Initializes the components and models a controller will be using. + * Triggers the controller action, and invokes the rendering if Controller::$autoRender is true and echo's the output. + * Otherwise the return value of the controller action are returned. + * + * @param Controller $controller Controller to invoke + * @param CakeRequest $request The request object to invoke the controller for. + * @param CakeResponse $response The response object to receive the output + * @return void + */ + protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) { + $controller->constructClasses(); + $controller->startupProcess(); + + $render = true; + $result = $controller->invokeAction($request); + if ($result instanceof CakeResponse) { + $render = false; + $response = $result; + } + + if ($render && $controller->autoRender) { + $response = $controller->render(); + } elseif ($response->body() === null) { + $response->body($result); + } + $controller->shutdownProcess(); + + if (isset($request->params['return'])) { + return $response->body(); + } + $response->send(); + } + +/** + * Applies Routing and additionalParameters to the request to be dispatched. + * If Routes have not been loaded they will be loaded, and app/Config/routes.php will be run. + * + * @param CakeRequest $request CakeRequest object to mine for parameter information. + * @param array $additionalParams An array of additional parameters to set to the request. + * Useful when Object::requestAction() is involved + * @return CakeRequest The request object with routing params set. + */ + public function parseParams(CakeRequest $request, $additionalParams = array()) { + if (count(Router::$routes) == 0) { + $namedExpressions = Router::getNamedExpressions(); + extract($namedExpressions); + $this->_loadRoutes(); + } + + $params = Router::parse($request->url); + $request->addParams($params); + + if (!empty($additionalParams)) { + $request->addParams($additionalParams); + } + return $request; + } + +/** + * Get controller to use, either plugin controller or application controller + * + * @param CakeRequest $request Request object + * @param CakeResponse $response Response for the controller. + * @return mixed name of controller if not loaded, or object if loaded + */ + protected function _getController($request, $response) { + $ctrlClass = $this->_loadController($request); + if (!$ctrlClass) { + return false; + } + return new $ctrlClass($request, $response); + } + +/** + * Load controller and return controller classname + * + * @param CakeRequest $request + * @return string|bool Name of controller class name + */ + protected function _loadController($request) { + $pluginName = $pluginPath = $controller = null; + if (!empty($request->params['plugin'])) { + $pluginName = $controller = Inflector::camelize($request->params['plugin']); + $pluginPath = $pluginName . '.'; + } + if (!empty($request->params['controller'])) { + $controller = Inflector::camelize($request->params['controller']); + } + if ($pluginPath . $controller) { + $class = $controller . 'Controller'; + App::uses('AppController', 'Controller'); + App::uses($pluginName . 'AppController', $pluginPath . 'Controller'); + App::uses($class, $pluginPath . 'Controller'); + if (class_exists($class)) { + return $class; + } + } + return false; + } + +/** + * Loads route configuration + * + * @return void + */ + protected function _loadRoutes() { + include APP . 'Config' . DS . 'routes.php'; + } + +/** + * Outputs cached dispatch view cache + * + * @param string $path Requested URL path + * @return string|boolean False if is not cached or output + */ + public function cached($path) { + if (Configure::read('Cache.check') === true) { + if ($path == '/') { + $path = 'home'; + } + $path = strtolower(Inflector::slug($path)); + + $filename = CACHE . 'views' . DS . $path . '.php'; + + if (!file_exists($filename)) { + $filename = CACHE . 'views' . DS . $path . '_index.php'; + } + + if (file_exists($filename)) { + $controller = null; + $view = new View($controller); + return $view->renderCache($filename, microtime(true)); + } + } + return false; + } + +/** + * Checks if a requested asset exists and sends it to the browser + * + * @param string $url Requested URL + * @param CakeResponse $response The response object to put the file contents in. + * @return boolean True on success if the asset file was found and sent + */ + public function asset($url, CakeResponse $response) { + if (strpos($url, '..') !== false || strpos($url, '.') === false) { + return false; + } + $filters = Configure::read('Asset.filter'); + $isCss = ( + strpos($url, 'ccss/') === 0 || + preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?statusCode(404); + $response->send(); + return true; + } elseif ($isCss) { + include WWW_ROOT . DS . $filters['css']; + return true; + } elseif ($isJs) { + include WWW_ROOT . DS . $filters['js']; + return true; + } + $pathSegments = explode('.', $url); + $ext = array_pop($pathSegments); + $parts = explode('/', $url); + $assetFile = null; + + if ($parts[0] === 'theme') { + $themeName = $parts[1]; + unset($parts[0], $parts[1]); + $fileFragment = implode(DS, $parts); + $path = App::themePath($themeName) . 'webroot' . DS; + if (file_exists($path . $fileFragment)) { + $assetFile = $path . $fileFragment; + } + } else { + $plugin = Inflector::camelize($parts[0]); + if (CakePlugin::loaded($plugin)) { + unset($parts[0]); + $fileFragment = implode(DS, $parts); + $pluginWebroot = CakePlugin::path($plugin) . 'webroot' . DS; + if (file_exists($pluginWebroot . $fileFragment)) { + $assetFile = $pluginWebroot . $fileFragment; + } + } + } + + if ($assetFile !== null) { + $this->_deliverAsset($response, $assetFile, $ext); + return true; + } + return false; + } + +/** + * Sends an asset file to the client + * + * @param CakeResponse $response The response object to use. + * @param string $assetFile Path to the asset file in the file system + * @param string $ext The extension of the file to determine its mime type + * @return void + */ + protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) { + ob_start(); + $compressionEnabled = Configure::read('Asset.compress') && $response->compress(); + if ($response->type($ext) == $ext) { + $contentType = 'application/octet-stream'; + $agent = env('HTTP_USER_AGENT'); + if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { + $contentType = 'application/octetstream'; + } + $response->type($contentType); + } + $response->cache(filemtime($assetFile)); + $response->send(); + ob_clean(); + if ($ext === 'css' || $ext === 'js') { + include($assetFile); + } else { + readfile($assetFile); + } + + if ($compressionEnabled) { + ob_end_flush(); + } + } +} diff --git a/app/Cake/Routing/Route/CakeRoute.php b/app/Cake/Routing/Route/CakeRoute.php new file mode 100644 index 00000000..0af11e33 --- /dev/null +++ b/app/Cake/Routing/Route/CakeRoute.php @@ -0,0 +1,506 @@ + 'content_type', + 'method' => 'request_method', + 'server' => 'server_name' + ); + +/** + * Constructor for a Route + * + * @param string $template Template string with parameter placeholders + * @param array $defaults Array of defaults for the route. + * @param array $options Array of additional options for the Route + */ + public function __construct($template, $defaults = array(), $options = array()) { + $this->template = $template; + $this->defaults = (array)$defaults; + $this->options = (array)$options; + } + +/** + * Check if a Route has been compiled into a regular expression. + * + * @return boolean + */ + public function compiled() { + return !empty($this->_compiledRoute); + } + +/** + * Compiles the route's regular expression. Modifies defaults property so all necessary keys are set + * and populates $this->names with the named routing elements. + * + * @return array Returns a string regular expression of the compiled route. + */ + public function compile() { + if ($this->compiled()) { + return $this->_compiledRoute; + } + $this->_writeRoute(); + return $this->_compiledRoute; + } + +/** + * Builds a route regular expression. Uses the template, defaults and options + * properties to compile a regular expression that can be used to parse request strings. + * + * @return void + */ + protected function _writeRoute() { + if (empty($this->template) || ($this->template === '/')) { + $this->_compiledRoute = '#^/*$#'; + $this->keys = array(); + return; + } + $route = $this->template; + $names = $routeParams = array(); + $parsed = preg_quote($this->template, '#'); + + preg_match_all('#:([A-Za-z0-9_-]+[A-Z0-9a-z])#', $route, $namedElements); + foreach ($namedElements[1] as $i => $name) { + $search = '\\' . $namedElements[0][$i]; + if (isset($this->options[$name])) { + $option = null; + if ($name !== 'plugin' && array_key_exists($name, $this->defaults)) { + $option = '?'; + } + $slashParam = '/\\' . $namedElements[0][$i]; + if (strpos($parsed, $slashParam) !== false) { + $routeParams[$slashParam] = '(?:/(?P<' . $name . '>' . $this->options[$name] . ')' . $option . ')' . $option; + } else { + $routeParams[$search] = '(?:(?P<' . $name . '>' . $this->options[$name] . ')' . $option . ')' . $option; + } + } else { + $routeParams[$search] = '(?:(?P<' . $name . '>[^/]+))'; + } + $names[] = $name; + } + if (preg_match('#\/\*$#', $route)) { + $parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed); + $this->_greedy = true; + } + krsort($routeParams); + $parsed = str_replace(array_keys($routeParams), array_values($routeParams), $parsed); + $this->_compiledRoute = '#^' . $parsed . '[/]*$#'; + $this->keys = $names; + + //remove defaults that are also keys. They can cause match failures + foreach ($this->keys as $key) { + unset($this->defaults[$key]); + } + } + +/** + * Checks to see if the given URL can be parsed by this route. + * If the route can be parsed an array of parameters will be returned; if not + * false will be returned. String urls are parsed if they match a routes regular expression. + * + * @param string $url The url to attempt to parse. + * @return mixed Boolean false on failure, otherwise an array or parameters + */ + public function parse($url) { + if (!$this->compiled()) { + $this->compile(); + } + if (!preg_match($this->_compiledRoute, $url, $route)) { + return false; + } + foreach ($this->defaults as $key => $val) { + if ($key[0] === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) { + if (isset($this->_headerMap[$header[1]])) { + $header = $this->_headerMap[$header[1]]; + } else { + $header = 'http_' . $header[1]; + } + $header = strtoupper($header); + + $val = (array)$val; + $h = false; + + foreach ($val as $v) { + if (env($header) === $v) { + $h = true; + } + } + if (!$h) { + return false; + } + } + } + array_shift($route); + $count = count($this->keys); + for ($i = 0; $i <= $count; $i++) { + unset($route[$i]); + } + $route['pass'] = $route['named'] = array(); + + // Assign defaults, set passed args to pass + foreach ($this->defaults as $key => $value) { + if (isset($route[$key])) { + continue; + } + if (is_integer($key)) { + $route['pass'][] = $value; + continue; + } + $route[$key] = $value; + } + + if (isset($route['_args_'])) { + list($pass, $named) = $this->_parseArgs($route['_args_'], $route); + $route['pass'] = array_merge($route['pass'], $pass); + $route['named'] = $named; + unset($route['_args_']); + } + + // restructure 'pass' key route params + if (isset($this->options['pass'])) { + $j = count($this->options['pass']); + while($j--) { + if (isset($route[$this->options['pass'][$j]])) { + array_unshift($route['pass'], $route[$this->options['pass'][$j]]); + } + } + } + return $route; + } + +/** + * Parse passed and Named parameters into a list of passed args, and a hash of named parameters. + * The local and global configuration for named parameters will be used. + * + * @param string $args A string with the passed & named params. eg. /1/page:2 + * @param string $context The current route context, which should contain controller/action keys. + * @return array Array of ($pass, $named) + */ + protected function _parseArgs($args, $context) { + $pass = $named = array(); + $args = explode('/', $args); + + $namedConfig = Router::namedConfig(); + $greedy = $namedConfig['greedyNamed']; + $rules = $namedConfig['rules']; + if (!empty($this->options['named'])) { + $greedy = isset($this->options['greedyNamed']) && $this->options['greedyNamed'] === true; + foreach ((array)$this->options['named'] as $key => $val) { + if (is_numeric($key)) { + $rules[$val] = true; + continue; + } + $rules[$key] = $val; + } + } + + foreach ($args as $param) { + if (empty($param) && $param !== '0' && $param !== 0) { + continue; + } + + $separatorIsPresent = strpos($param, $namedConfig['separator']) !== false; + if ((!isset($this->options['named']) || !empty($this->options['named'])) && $separatorIsPresent) { + list($key, $val) = explode($namedConfig['separator'], $param, 2); + $hasRule = isset($rules[$key]); + $passIt = (!$hasRule && !$greedy) || ($hasRule && !$this->_matchNamed($val, $rules[$key], $context)); + if ($passIt) { + $pass[] = $param; + } else { + if (preg_match_all('/\[([A-Za-z0-9_-]+)?\]/', $key, $matches, PREG_SET_ORDER)) { + $matches = array_reverse($matches); + $parts = explode('[', $key); + $key = array_shift($parts); + $arr = $val; + foreach ($matches as $match) { + if (empty($match[1])) { + $arr = array($arr); + } else { + $arr = array( + $match[1] => $arr + ); + } + } + $val = $arr; + } + $named = array_merge_recursive($named, array($key => $val)); + } + } else { + $pass[] = $param; + } + } + return array($pass, $named); + } + +/** + * Return true if a given named $param's $val matches a given $rule depending on $context. Currently implemented + * rule types are controller, action and match that can be combined with each other. + * + * @param string $val The value of the named parameter + * @param array $rule The rule(s) to apply, can also be a match string + * @param string $context An array with additional context information (controller / action) + * @return boolean + */ + protected function _matchNamed($val, $rule, $context) { + if ($rule === true || $rule === false) { + return $rule; + } + if (is_string($rule)) { + $rule = array('match' => $rule); + } + if (!is_array($rule)) { + return false; + } + + $controllerMatches = ( + !isset($rule['controller'], $context['controller']) || + in_array($context['controller'], (array)$rule['controller']) + ); + if (!$controllerMatches) { + return false; + } + $actionMatches = ( + !isset($rule['action'], $context['action']) || + in_array($context['action'], (array)$rule['action']) + ); + if (!$actionMatches) { + return false; + } + return (!isset($rule['match']) || preg_match('/' . $rule['match'] . '/', $val)); + } + +/** + * Apply persistent parameters to a url array. Persistant parameters are a special + * key used during route creation to force route parameters to persist when omitted from + * a url array. + * + * @param array $url The array to apply persistent parameters to. + * @param array $params An array of persistent values to replace persistent ones. + * @return array An array with persistent parameters applied. + */ + public function persistParams($url, $params) { + foreach ($this->options['persist'] as $persistKey) { + if (array_key_exists($persistKey, $params) && !isset($url[$persistKey])) { + $url[$persistKey] = $params[$persistKey]; + } + } + return $url; + } + +/** + * Attempt to match a url array. If the url matches the route parameters and settings, then + * return a generated string url. If the url doesn't match the route parameters, false will be returned. + * This method handles the reverse routing or conversion of url arrays into string urls. + * + * @param array $url An array of parameters to check matching with. + * @return mixed Either a string url for the parameters if they match or false. + */ + public function match($url) { + if (!$this->compiled()) { + $this->compile(); + } + $defaults = $this->defaults; + + if (isset($defaults['prefix'])) { + $url['prefix'] = $defaults['prefix']; + } + + //check that all the key names are in the url + $keyNames = array_flip($this->keys); + if (array_intersect_key($keyNames, $url) !== $keyNames) { + return false; + } + + // Missing defaults is a fail. + if (array_diff_key($defaults, $url) !== array()) { + return false; + } + + $namedConfig = Router::namedConfig(); + $greedyNamed = $namedConfig['greedyNamed']; + $allowedNamedParams = $namedConfig['rules']; + + $named = $pass = array(); + + foreach ($url as $key => $value) { + + // keys that exist in the defaults and have different values is a match failure. + $defaultExists = array_key_exists($key, $defaults); + if ($defaultExists && $defaults[$key] != $value) { + return false; + } elseif ($defaultExists) { + continue; + } + + // If the key is a routed key, its not different yet. + if (array_key_exists($key, $keyNames)) { + continue; + } + + // pull out passed args + $numeric = is_numeric($key); + if ($numeric && isset($defaults[$key]) && $defaults[$key] == $value) { + continue; + } elseif ($numeric) { + $pass[] = $value; + unset($url[$key]); + continue; + } + + // pull out named params if named params are greedy or a rule exists. + if ( + ($greedyNamed || isset($allowedNamedParams[$key])) && + ($value !== false && $value !== null) + ) { + $named[$key] = $value; + continue; + } + + // keys that don't exist are different. + if (!$defaultExists && !empty($value)) { + return false; + } + } + + //if a not a greedy route, no extra params are allowed. + if (!$this->_greedy && (!empty($pass) || !empty($named))) { + return false; + } + + //check patterns for routed params + if (!empty($this->options)) { + foreach ($this->options as $key => $pattern) { + if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) { + return false; + } + } + } + return $this->_writeUrl(array_merge($url, compact('pass', 'named'))); + } + +/** + * Converts a matching route array into a url string. Composes the string url using the template + * used to create the route. + * + * @param array $params The params to convert to a string url. + * @return string Composed route string. + */ + protected function _writeUrl($params) { + if (isset($params['prefix'], $params['action'])) { + $params['action'] = str_replace($params['prefix'] . '_', '', $params['action']); + unset($params['prefix']); + } + + if (is_array($params['pass'])) { + $params['pass'] = implode('/', $params['pass']); + } + + $namedConfig = Router::namedConfig(); + $separator = $namedConfig['separator']; + + if (!empty($params['named']) && is_array($params['named'])) { + $named = array(); + foreach ($params['named'] as $key => $value) { + if (is_array($value)) { + foreach ($value as $namedKey => $namedValue) { + $named[] = $key . "[$namedKey]" . $separator . $namedValue; + } + } else { + $named[] = $key . $separator . $value; + } + } + $params['pass'] = $params['pass'] . '/' . implode('/', $named); + } + $out = $this->template; + + $search = $replace = array(); + foreach ($this->keys as $key) { + $string = null; + if (isset($params[$key])) { + $string = $params[$key]; + } elseif (strpos($out, $key) != strlen($out) - strlen($key)) { + $key .= '/'; + } + $search[] = ':' . $key; + $replace[] = $string; + } + $out = str_replace($search, $replace, $out); + + if (strpos($this->template, '*')) { + $out = str_replace('*', $params['pass'], $out); + } + $out = str_replace('//', '/', $out); + return $out; + } + +} diff --git a/app/Cake/Routing/Route/PluginShortRoute.php b/app/Cake/Routing/Route/PluginShortRoute.php new file mode 100644 index 00000000..7ae7a6bc --- /dev/null +++ b/app/Cake/Routing/Route/PluginShortRoute.php @@ -0,0 +1,55 @@ +defaults['controller'] = $url['controller']; + $result = parent::match($url); + unset($this->defaults['controller']); + return $result; + } +} \ No newline at end of file diff --git a/app/Cake/Routing/Route/RedirectRoute.php b/app/Cake/Routing/Route/RedirectRoute.php new file mode 100644 index 00000000..82e1697b --- /dev/null +++ b/app/Cake/Routing/Route/RedirectRoute.php @@ -0,0 +1,93 @@ +redirect = (array)$defaults; + } + +/** + * Parses a string url into an array. Parsed urls will result in an automatic + * redirection + * + * @param string $url The url to parse + * @return boolean False on failure + */ + public function parse($url) { + $params = parent::parse($url); + if (!$params) { + return false; + } + if (!$this->response) { + $this->response = new CakeResponse(); + } + $redirect = $this->redirect; + if (count($this->redirect) == 1 && !isset($this->redirect['controller'])) { + $redirect = $this->redirect[0]; + } + if (isset($this->options['persist']) && is_array($redirect)) { + $redirect += array('named' => $params['named'], 'pass' => $params['pass'], 'url' => array()); + $redirect = Router::reverse($redirect); + } + $status = 301; + if (isset($this->options['status']) && ($this->options['status'] >= 300 && $this->options['status'] < 400)) { + $status = $this->options['status']; + } + $this->response->header(array('Location' => Router::url($redirect, true))); + $this->response->statusCode($status); + $this->response->send(); + } + +/** + * There is no reverse routing redirection routes + * + * @param array $url Array of parameters to convert to a string. + * @return mixed either false or a string url. + */ + public function match($url) { + return false; + } +} \ No newline at end of file diff --git a/app/Cake/Routing/Router.php b/app/Cake/Routing/Router.php new file mode 100644 index 00000000..d1e5d4eb --- /dev/null +++ b/app/Cake/Routing/Router.php @@ -0,0 +1,1062 @@ + Router::ACTION, + 'Year' => Router::YEAR, + 'Month' => Router::MONTH, + 'Day' => Router::DAY, + 'ID' => Router::ID, + 'UUID' => Router::UUID + ); + +/** + * Stores all information necessary to decide what named arguments are parsed under what conditions. + * + * @var string + */ + protected static $_namedConfig = array( + 'default' => array('page', 'fields', 'order', 'limit', 'recursive', 'sort', 'direction', 'step'), + 'greedyNamed' => true, + 'separator' => ':', + 'rules' => false, + ); + +/** + * The route matching the URL of the current request + * + * @var array + */ + protected static $_currentRoute = array(); + +/** + * Default HTTP request method => controller action map. + * + * @var array + */ + protected static $_resourceMap = array( + array('action' => 'index', 'method' => 'GET', 'id' => false), + array('action' => 'view', 'method' => 'GET', 'id' => true), + array('action' => 'add', 'method' => 'POST', 'id' => false), + array('action' => 'edit', 'method' => 'PUT', 'id' => true), + array('action' => 'delete', 'method' => 'DELETE', 'id' => true), + array('action' => 'edit', 'method' => 'POST', 'id' => true) + ); + +/** + * List of resource-mapped controllers + * + * @var array + */ + protected static $_resourceMapped = array(); + +/** + * Maintains the request object stack for the current request. + * This will contain more than one request object when requestAction is used. + * + * @var array + */ + protected static $_requests = array(); + +/** + * Initial state is popualated the first time reload() is called which is at the bottom + * of this file. This is a cheat as get_class_vars() returns the value of static vars even if they + * have changed. + * + * @var array + */ + protected static $_initialState = array(); + +/** + * Sets the Routing prefixes. + * + * @return void + */ + protected static function _setPrefixes() { + $routing = Configure::read('Routing'); + if (!empty($routing['prefixes'])) { + self::$_prefixes = array_merge(self::$_prefixes, (array)$routing['prefixes']); + } + } + +/** + * Gets the named route elements for use in app/Config/routes.php + * + * @return array Named route elements + * @see Router::$_namedExpressions + */ + public static function getNamedExpressions() { + return self::$_namedExpressions; + } + +/** + * Connects a new Route in the router. + * + * Routes are a way of connecting request urls to objects in your application. At their core routes + * are a set or regular expressions that are used to match requests to destinations. + * + * Examples: + * + * `Router::connect('/:controller/:action/*');` + * + * The first parameter will be used as a controller name while the second is used as the action name. + * the '/*' syntax makes this route greedy in that it will match requests like `/posts/index` as well as requests + * like `/posts/edit/1/foo/bar`. + * + * `Router::connect('/home-page', array('controller' => 'pages', 'action' => 'display', 'home'));` + * + * The above shows the use of route parameter defaults. And providing routing parameters for a static route. + * + * {{{ + * Router::connect( + * '/:lang/:controller/:action/:id', + * array(), + * array('id' => '[0-9]+', 'lang' => '[a-z]{3}') + * ); + * }}} + * + * Shows connecting a route with custom route parameters as well as providing patterns for those parameters. + * Patterns for routing parameters do not need capturing groups, as one will be added for each route params. + * + * $options offers four 'special' keys. `pass`, `named`, `persist` and `routeClass` + * have special meaning in the $options array. + * + * `pass` is used to define which of the routed parameters should be shifted into the pass array. Adding a + * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')` + * + * `persist` is used to define which route parameters should be automatically included when generating + * new urls. You can override persistent parameters by redefining them in a url or remove them by + * setting the parameter to `false`. Ex. `'persist' => array('lang')` + * + * `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing, + * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'` + * + * `named` is used to configure named parameters at the route level. This key uses the same options + * as Router::connectNamed() + * + * @param string $route A string describing the template of the route + * @param array $defaults An array describing the default route parameters. These parameters will be used by default + * and can supply routing parameters that are not dynamic. See above. + * @param array $options An array matching the named elements in the route to regular expressions which that + * element should match. Also contains additional parameters such as which routed parameters should be + * shifted into the passed arguments, supplying patterns for routing parameters and supplying the name of a + * custom routing class. + * @see routes + * @return array Array of routes + * @throws RouterException + */ + public static function connect($route, $defaults = array(), $options = array()) { + foreach (self::$_prefixes as $prefix) { + if (isset($defaults[$prefix])) { + $defaults['prefix'] = $prefix; + break; + } + } + if (isset($defaults['prefix'])) { + self::$_prefixes[] = $defaults['prefix']; + self::$_prefixes = array_keys(array_flip(self::$_prefixes)); + } + $defaults += array('plugin' => null); + if (empty($options['action'])) { + $defaults += array('action' => 'index'); + } + $routeClass = 'CakeRoute'; + if (isset($options['routeClass'])) { + $routeClass = $options['routeClass']; + if (!is_subclass_of($routeClass, 'CakeRoute')) { + throw new RouterException(__d('cake_dev', 'Route classes must extend CakeRoute')); + } + unset($options['routeClass']); + if ($routeClass == 'RedirectRoute' && isset($defaults['redirect'])) { + $defaults = $defaults['redirect']; + } + } + self::$routes[] = new $routeClass($route, $defaults, $options); + return self::$routes; + } + +/** + * Connects a new redirection Route in the router. + * + * Redirection routes are different from normal routes as they perform an actual + * header redirection if a match is found. The redirection can occur within your + * application or redirect to an outside location. + * + * Examples: + * + * `Router::redirect('/home/*', array('controller' => 'posts', 'action' => 'view', array('persist' => true));` + * + * Redirects /home/* to /posts/view and passes the parameters to /posts/view. Using an array as the + * redirect destination allows you to use other routes to define where a url string should be redirected to. + * + * `Router::redirect('/posts/*', 'http://google.com', array('status' => 302));` + * + * Redirects /posts/* to http://google.com with a HTTP status of 302 + * + * ### Options: + * + * - `status` Sets the HTTP status (default 301) + * - `persist` Passes the params to the redirected route, if it can. This is useful with greedy routes, + * routes that end in `*` are greedy. As you can remap urls and not loose any passed/named args. + * + * @param string $route A string describing the template of the route + * @param array $url A url to redirect to. Can be a string or a Cake array-based url + * @param array $options An array matching the named elements in the route to regular expressions which that + * element should match. Also contains additional parameters such as which routed parameters should be + * shifted into the passed arguments. As well as supplying patterns for routing parameters. + * @see routes + * @return array Array of routes + */ + public static function redirect($route, $url, $options = array()) { + App::uses('RedirectRoute', 'Routing/Route'); + $options['routeClass'] = 'RedirectRoute'; + if (is_string($url)) { + $url = array('redirect' => $url); + } + return self::connect($route, $url, $options); + } + +/** + * Specifies what named parameters CakePHP should be parsing out of incoming urls. By default + * CakePHP will parse every named parameter out of incoming URLs. However, if you want to take more + * control over how named parameters are parsed you can use one of the following setups: + * + * Do not parse any named parameters: + * + * {{{ Router::connectNamed(false); }}} + * + * Parse only default parameters used for CakePHP's pagination: + * + * {{{ Router::connectNamed(false, array('default' => true)); }}} + * + * Parse only the page parameter if its value is a number: + * + * {{{ Router::connectNamed(array('page' => '[\d]+'), array('default' => false, 'greedy' => false)); }}} + * + * Parse only the page parameter no matter what. + * + * {{{ Router::connectNamed(array('page'), array('default' => false, 'greedy' => false)); }}} + * + * Parse only the page parameter if the current action is 'index'. + * + * {{{ + * Router::connectNamed( + * array('page' => array('action' => 'index')), + * array('default' => false, 'greedy' => false) + * ); + * }}} + * + * Parse only the page parameter if the current action is 'index' and the controller is 'pages'. + * + * {{{ + * Router::connectNamed( + * array('page' => array('action' => 'index', 'controller' => 'pages')), + * array('default' => false, 'greedy' => false) + * ); + * }}} + * + * ### Options + * + * - `greedy` Setting this to true will make Router parse all named params. Setting it to false will + * parse only the connected named params. + * - `default` Set this to true to merge in the default set of named parameters. + * - `reset` Set to true to clear existing rules and start fresh. + * - `separator` Change the string used to separate the key & value in a named parameter. Defaults to `:` + * + * @param array $named A list of named parameters. Key value pairs are accepted where values are + * either regex strings to match, or arrays as seen above. + * @param array $options Allows to control all settings: separator, greedy, reset, default + * @return array + */ + public static function connectNamed($named, $options = array()) { + if (isset($options['separator'])) { + self::$_namedConfig['separator'] = $options['separator']; + unset($options['separator']); + } + + if ($named === true || $named === false) { + $options = array_merge(array('default' => $named, 'reset' => true, 'greedy' => $named), $options); + $named = array(); + } else { + $options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options); + } + + if ($options['reset'] == true || self::$_namedConfig['rules'] === false) { + self::$_namedConfig['rules'] = array(); + } + + if ($options['default']) { + $named = array_merge($named, self::$_namedConfig['default']); + } + + foreach ($named as $key => $val) { + if (is_numeric($key)) { + self::$_namedConfig['rules'][$val] = true; + } else { + self::$_namedConfig['rules'][$key] = $val; + } + } + self::$_namedConfig['greedyNamed'] = $options['greedy']; + return self::$_namedConfig; + } + +/** + * Gets the current named parameter configuration values. + * + * @return array + * @see Router::$_namedConfig + */ + public static function namedConfig() { + return self::$_namedConfig; + } + +/** + * Creates REST resource routes for the given controller(s). When creating resource routes + * for a plugin, by default the prefix will be changed to the lower_underscore version of the plugin + * name. By providing a prefix you can override this behavior. + * + * ### Options: + * + * - 'id' - The regular expression fragment to use when matching IDs. By default, matches + * integer values and UUIDs. + * - 'prefix' - URL prefix to use for the generated routes. Defaults to '/'. + * + * @param mixed $controller A controller name or array of controller names (i.e. "Posts" or "ListItems") + * @param array $options Options to use when generating REST routes + * @return array Array of mapped resources + */ + public static function mapResources($controller, $options = array()) { + $hasPrefix = isset($options['prefix']); + $options = array_merge(array( + 'prefix' => '/', + 'id' => self::ID . '|' . self::UUID + ), $options); + + $prefix = $options['prefix']; + + foreach ((array)$controller as $name) { + list($plugin, $name) = pluginSplit($name); + $urlName = Inflector::underscore($name); + $plugin = Inflector::underscore($plugin); + if ($plugin && !$hasPrefix) { + $prefix = '/' . $plugin . '/'; + } + + foreach (self::$_resourceMap as $params) { + $url = $prefix . $urlName . (($params['id']) ? '/:id' : ''); + + Router::connect($url, + array( + 'plugin' => $plugin, + 'controller' => $urlName, + 'action' => $params['action'], + '[method]' => $params['method'] + ), + array('id' => $options['id'], 'pass' => array('id')) + ); + } + self::$_resourceMapped[] = $urlName; + } + return self::$_resourceMapped; + } + +/** + * Returns the list of prefixes used in connected routes + * + * @return array A list of prefixes used in connected routes + */ + public static function prefixes() { + return self::$_prefixes; + } + +/** + * Parses given URL string. Returns 'routing' parameters for that url. + * + * @param string $url URL to be parsed + * @return array Parsed elements from URL + */ + public static function parse($url) { + $ext = null; + $out = array(); + + if ($url && strpos($url, '/') !== 0) { + $url = '/' . $url; + } + if (strpos($url, '?') !== false) { + $url = substr($url, 0, strpos($url, '?')); + } + + extract(self::_parseExtension($url)); + + for ($i = 0, $len = count(self::$routes); $i < $len; $i++) { + $route =& self::$routes[$i]; + + if (($r = $route->parse($url)) !== false) { + self::$_currentRoute[] =& $route; + $out = $r; + break; + } + } + if (isset($out['prefix'])) { + $out['action'] = $out['prefix'] . '_' . $out['action']; + } + + if (!empty($ext) && !isset($out['ext'])) { + $out['ext'] = $ext; + } + return $out; + } + +/** + * Parses a file extension out of a URL, if Router::parseExtensions() is enabled. + * + * @param string $url + * @return array Returns an array containing the altered URL and the parsed extension. + */ + protected static function _parseExtension($url) { + $ext = null; + + if (self::$_parseExtensions) { + if (preg_match('/\.[0-9a-zA-Z]*$/', $url, $match) === 1) { + $match = substr($match[0], 1); + if (empty(self::$_validExtensions)) { + $url = substr($url, 0, strpos($url, '.' . $match)); + $ext = $match; + } else { + foreach (self::$_validExtensions as $name) { + if (strcasecmp($name, $match) === 0) { + $url = substr($url, 0, strpos($url, '.' . $name)); + $ext = $match; + break; + } + } + } + } + } + return compact('ext', 'url'); + } + +/** + * Takes parameter and path information back from the Dispatcher, sets these + * parameters as the current request parameters that are merged with url arrays + * created later in the request. + * + * Nested requests will create a stack of requests. You can remove requests using + * Router::popRequest(). This is done automatically when using Object::requestAction(). + * + * Will accept either a CakeRequest object or an array of arrays. Support for + * accepting arrays may be removed in the future. + * + * @param CakeRequest|array $request Parameters and path information or a CakeRequest object. + * @return void + */ + public static function setRequestInfo($request) { + if ($request instanceof CakeRequest) { + self::$_requests[] = $request; + } else { + $requestObj = new CakeRequest(); + $request += array(array(), array()); + $request[0] += array('controller' => false, 'action' => false, 'plugin' => null); + $requestObj->addParams($request[0])->addPaths($request[1]); + self::$_requests[] = $requestObj; + } + } + +/** + * Pops a request off of the request stack. Used when doing requestAction + * + * @return CakeRequest The request removed from the stack. + * @see Router::setRequestInfo() + * @see Object::requestAction() + */ + public static function popRequest() { + return array_pop(self::$_requests); + } + +/** + * Get the either the current request object, or the first one. + * + * @param boolean $current Whether you want the request from the top of the stack or the first one. + * @return CakeRequest or null. + */ + public static function getRequest($current = false) { + if ($current) { + return self::$_requests[count(self::$_requests) - 1]; + } + return isset(self::$_requests[0]) ? self::$_requests[0] : null; + } + +/** + * Gets parameter information + * + * @param boolean $current Get current request parameter, useful when using requestAction + * @return array Parameter information + */ + public static function getParams($current = false) { + if ($current) { + return self::$_requests[count(self::$_requests) - 1]->params; + } + if (isset(self::$_requests[0])) { + return self::$_requests[0]->params; + } + return array(); + } + +/** + * Gets URL parameter by name + * + * @param string $name Parameter name + * @param boolean $current Current parameter, useful when using requestAction + * @return string Parameter value + */ + public static function getParam($name = 'controller', $current = false) { + $params = Router::getParams($current); + if (isset($params[$name])) { + return $params[$name]; + } + return null; + } + +/** + * Gets path information + * + * @param boolean $current Current parameter, useful when using requestAction + * @return array + */ + public static function getPaths($current = false) { + if ($current) { + return self::$_requests[count(self::$_requests) - 1]; + } + if (!isset(self::$_requests[0])) { + return array('base' => null); + } + return array('base' => self::$_requests[0]->base); + } + +/** + * Reloads default Router settings. Resets all class variables and + * removes all connected routes. + * + * @return void + */ + public static function reload() { + if (empty(self::$_initialState)) { + self::$_initialState = get_class_vars('Router'); + self::_setPrefixes(); + return; + } + foreach (self::$_initialState as $key => $val) { + if ($key != '_initialState') { + self::${$key} = $val; + } + } + self::_setPrefixes(); + } + +/** + * Promote a route (by default, the last one added) to the beginning of the list + * + * @param integer $which A zero-based array index representing the route to move. For example, + * if 3 routes have been added, the last route would be 2. + * @return boolean Returns false if no route exists at the position specified by $which. + */ + public static function promote($which = null) { + if ($which === null) { + $which = count(self::$routes) - 1; + } + if (!isset(self::$routes[$which])) { + return false; + } + $route =& self::$routes[$which]; + unset(self::$routes[$which]); + array_unshift(self::$routes, $route); + return true; + } + +/** + * Finds URL for specified action. + * + * Returns an URL pointing to a combination of controller and action. Param + * $url can be: + * + * - Empty - the method will find address to actual controller/action. + * - '/' - the method will find base URL of application. + * - A combination of controller/action - the method will find url for it. + * + * There are a few 'special' parameters that can change the final URL string that is generated + * + * - `base` - Set to false to remove the base path from the generated url. If your application + * is not in the root directory, this can be used to generate urls that are 'cake relative'. + * cake relative urls are required when using requestAction. + * - `?` - Takes an array of query string parameters + * - `#` - Allows you to set url hash fragments. + * - `full_base` - If true the `FULL_BASE_URL` constant will be prepended to generated urls. + * + * @param mixed $url Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4" + * or an array specifying any of the following: 'controller', 'action', + * and/or 'plugin', in addition to named arguments (keyed array elements), + * and standard URL arguments (indexed array elements) + * @param mixed $full If (bool) true, the full base URL will be prepended to the result. + * If an array accepts the following keys + * - escape - used when making urls embedded in html escapes query string '&' + * - full - if true the full base URL will be prepended. + * @return string Full translated URL with base path. + */ + public static function url($url = null, $full = false) { + $params = array('plugin' => null, 'controller' => null, 'action' => 'index'); + + if (is_bool($full)) { + $escape = false; + } else { + extract($full + array('escape' => false, 'full' => false)); + } + + $path = array('base' => null); + if (!empty(self::$_requests)) { + $request = self::$_requests[count(self::$_requests) - 1]; + $params = $request->params; + $path = array('base' => $request->base, 'here' => $request->here); + } + + $base = $path['base']; + $extension = $output = $q = $frag = null; + + if (empty($url)) { + $output = isset($path['here']) ? $path['here'] : '/'; + if ($full && defined('FULL_BASE_URL')) { + $output = FULL_BASE_URL . $output; + } + return $output; + } elseif (is_array($url)) { + if (isset($url['base']) && $url['base'] === false) { + $base = null; + unset($url['base']); + } + if (isset($url['full_base']) && $url['full_base'] === true) { + $full = true; + unset($url['full_base']); + } + if (isset($url['?'])) { + $q = $url['?']; + unset($url['?']); + } + if (isset($url['#'])) { + $frag = '#' . urlencode($url['#']); + unset($url['#']); + } + if (isset($url['ext'])) { + $extension = '.' . $url['ext']; + unset($url['ext']); + } + if (empty($url['action'])) { + if (empty($url['controller']) || $params['controller'] === $url['controller']) { + $url['action'] = $params['action']; + } else { + $url['action'] = 'index'; + } + } + + $prefixExists = (array_intersect_key($url, array_flip(self::$_prefixes))); + foreach (self::$_prefixes as $prefix) { + if (!empty($params[$prefix]) && !$prefixExists) { + $url[$prefix] = true; + } elseif (isset($url[$prefix]) && !$url[$prefix]) { + unset($url[$prefix]); + } + if (isset($url[$prefix]) && strpos($url['action'], $prefix . '_') === 0) { + $url['action'] = substr($url['action'], strlen($prefix) + 1); + } + } + + $url += array('controller' => $params['controller'], 'plugin' => $params['plugin']); + + $match = false; + + for ($i = 0, $len = count(self::$routes); $i < $len; $i++) { + $originalUrl = $url; + + if (isset(self::$routes[$i]->options['persist'], $params)) { + $url = self::$routes[$i]->persistParams($url, $params); + } + + if ($match = self::$routes[$i]->match($url)) { + $output = trim($match, '/'); + break; + } + $url = $originalUrl; + } + if ($match === false) { + $output = self::_handleNoRoute($url); + } + } else { + if ( + (strpos($url, '://') || + (strpos($url, 'javascript:') === 0) || + (strpos($url, 'mailto:') === 0)) || + (!strncmp($url, '#', 1)) + ) { + return $url; + } + if (substr($url, 0, 1) === '/') { + $output = substr($url, 1); + } else { + foreach (self::$_prefixes as $prefix) { + if (isset($params[$prefix])) { + $output .= $prefix . '/'; + break; + } + } + if (!empty($params['plugin']) && $params['plugin'] !== $params['controller']) { + $output .= Inflector::underscore($params['plugin']) . '/'; + } + $output .= Inflector::underscore($params['controller']) . '/' . $url; + } + } + $protocol = preg_match('#^[a-z][a-z0-9+-.]*\://#i', $output); + if ($protocol === 0) { + $output = str_replace('//', '/', $base . '/' . $output); + + if ($full && defined('FULL_BASE_URL')) { + $output = FULL_BASE_URL . $output; + } + if (!empty($extension)) { + $output = rtrim($output, '/'); + } + } + return $output . $extension . self::queryString($q, array(), $escape) . $frag; + } + +/** + * A special fallback method that handles url arrays that cannot match + * any defined routes. + * + * @param array $url A url that didn't match any routes + * @return string A generated url for the array + * @see Router::url() + */ + protected static function _handleNoRoute($url) { + $named = $args = $query = array(); + $skip = array_merge( + array('bare', 'action', 'controller', 'plugin', 'prefix'), + self::$_prefixes + ); + + $keys = array_values(array_diff(array_keys($url), $skip)); + $count = count($keys); + + // Remove this once parsed URL parameters can be inserted into 'pass' + for ($i = 0; $i < $count; $i++) { + $key = $keys[$i]; + if (is_numeric($keys[$i])) { + $args[] = $url[$key]; + } else { + $named[$key] = $url[$key]; + } + } + + list($args, $named) = array(Set::filter($args, true), Set::filter($named, true)); + foreach (self::$_prefixes as $prefix) { + if (!empty($url[$prefix])) { + $url['action'] = str_replace($prefix . '_', '', $url['action']); + break; + } + } + + if (empty($named) && empty($args) && empty($query) && (!isset($url['action']) || $url['action'] === 'index')) { + $url['action'] = null; + } + + $urlOut = array_filter(array($url['controller'], $url['action'])); + + if (isset($url['plugin'])) { + array_unshift($urlOut, $url['plugin']); + } + + foreach (self::$_prefixes as $prefix) { + if (isset($url[$prefix])) { + array_unshift($urlOut, $prefix); + break; + } + } + $output = implode('/', $urlOut); + + if (!empty($args)) { + $output .= '/' . implode('/', $args); + } + + if (!empty($named)) { + foreach ($named as $name => $value) { + if (is_array($value)) { + $flattend = Set::flatten($value, ']['); + foreach ($flattend as $namedKey => $namedValue) { + $output .= '/' . $name . "[$namedKey]" . self::$_namedConfig['separator'] . $namedValue; + } + } else { + $output .= '/' . $name . self::$_namedConfig['separator'] . $value; + } + } + } + if (!empty($query)) { + $output .= Router::queryString($query); + } + return $output; + } + +/** + * Generates a well-formed querystring from $q + * + * @param string|array $q Query string Either a string of already compiled query string arguments or + * an array of arguments to convert into a query string. + * @param array $extra Extra querystring parameters. + * @param boolean $escape Whether or not to use escaped & + * @return array + */ + public static function queryString($q, $extra = array(), $escape = false) { + if (empty($q) && empty($extra)) { + return null; + } + $join = '&'; + if ($escape === true) { + $join = '&'; + } + $out = ''; + + if (is_array($q)) { + $q = array_merge($extra, $q); + } else { + $out = $q; + $q = $extra; + } + $out .= http_build_query($q, null, $join); + if (isset($out[0]) && $out[0] != '?') { + $out = '?' . $out; + } + return $out; + } + +/** + * Reverses a parsed parameter array into a string. Works similarly to Router::url(), but + * Since parsed URL's contain additional 'pass' and 'named' as well as 'url.url' keys. + * Those keys need to be specially handled in order to reverse a params array into a string url. + * + * This will strip out 'autoRender', 'bare', 'requested', and 'return' param names as those + * are used for CakePHP internals and should not normally be part of an output url. + * + * @param CakeRequest|array $params The params array or CakeRequest object that needs to be reversed. + * @param boolean $full Set to true to include the full url including the protocol when reversing + * the url. + * @return string The string that is the reversed result of the array + */ + public static function reverse($params, $full = false) { + if ($params instanceof CakeRequest) { + $url = $params->query; + $params = $params->params; + } else { + $url = $params['url']; + } + $pass = isset($params['pass']) ? $params['pass'] : array(); + $named = isset($params['named']) ? $params['named'] : array(); + + unset( + $params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'], + $params['autoRender'], $params['bare'], $params['requested'], $params['return'], + $params['_Token'] + ); + $params = array_merge($params, $pass, $named); + if (!empty($url)) { + $params['?'] = $url; + } + return Router::url($params, $full); + } + +/** + * Normalizes a URL for purposes of comparison. Will strip the base path off + * and replace any double /'s. It will not unify the casing and underscoring + * of the input value. + * + * @param mixed $url URL to normalize Either an array or a string url. + * @return string Normalized URL + */ + public static function normalize($url = '/') { + if (is_array($url)) { + $url = Router::url($url); + } elseif (preg_match('/^[a-z\-]+:\/\//', $url)) { + return $url; + } + $request = Router::getRequest(); + + if (!empty($request->base) && stristr($url, $request->base)) { + $url = preg_replace('/^' . preg_quote($request->base, '/') . '/', '', $url, 1); + } + $url = '/' . $url; + + while (strpos($url, '//') !== false) { + $url = str_replace('//', '/', $url); + } + $url = preg_replace('/(?:(\/$))/', '', $url); + + if (empty($url)) { + return '/'; + } + return $url; + } + +/** + * Returns the route matching the current request URL. + * + * @return CakeRoute Matching route object. + */ + public static function &requestRoute() { + return self::$_currentRoute[0]; + } + +/** + * Returns the route matching the current request (useful for requestAction traces) + * + * @return CakeRoute Matching route object. + */ + public static function ¤tRoute() { + return self::$_currentRoute[count(self::$_currentRoute) - 1]; + } + +/** + * Removes the plugin name from the base URL. + * + * @param string $base Base URL + * @param string $plugin Plugin name + * @return string base url with plugin name removed if present + */ + public static function stripPlugin($base, $plugin = null) { + if ($plugin != null) { + $base = preg_replace('/(?:' . $plugin . ')/', '', $base); + $base = str_replace('//', '', $base); + $pos1 = strrpos($base, '/'); + $char = strlen($base) - 1; + + if ($pos1 === $char) { + $base = substr($base, 0, $char); + } + } + return $base; + } + +/** + * Instructs the router to parse out file extensions from the URL. For example, + * http://example.com/posts.rss would yield an file extension of "rss". + * The file extension itself is made available in the controller as + * $this->params['url']['ext'], and is used by the RequestHandler component to + * automatically switch to alternate layouts and templates, and load helpers + * corresponding to the given content, i.e. RssHelper. + * + * A list of valid extension can be passed to this method, i.e. Router::parseExtensions('rss', 'xml'); + * If no parameters are given, anything after the first . (dot) after the last / in the URL will be + * parsed, excluding querystring parameters (i.e. ?q=...). + * + * @return void + */ + public static function parseExtensions() { + self::$_parseExtensions = true; + if (func_num_args() > 0) { + self::$_validExtensions = func_get_args(); + } + } + +/** + * Get the list of extensions that can be parsed by Router. To add more + * extensions use Router::parseExtensions() + * + * @return array Array of extensions Router is configured to parse. + */ + public static function extensions() { + return self::$_validExtensions; + } + +} + +//Save the initial state +Router::reload(); diff --git a/app/Cake/Utility/ClassRegistry.php b/app/Cake/Utility/ClassRegistry.php new file mode 100644 index 00000000..11cc8a59 --- /dev/null +++ b/app/Cake/Utility/ClassRegistry.php @@ -0,0 +1,341 @@ + 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');``` + * + * Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);``` + * + * When $class is a numeric keyed array, multiple class instances will be stored in the registry, + * no instance of the object will be returned + * {{{ + * array( + * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'), + * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'), + * array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry') + * ); + * }}} + * @param mixed $class as a string or a single key => value array instance will be created, + * stored in the registry and returned. + * @param boolean $strict if set to true it will return false if the class was not found instead + * of trying to create an AppModel + * @return object instance of ClassName + */ + public static function init($class, $strict = false) { + $_this = ClassRegistry::getInstance(); + $false = false; + $true = true; + + if (is_array($class)) { + $objects = $class; + if (!isset($class[0])) { + $objects = array($class); + } + } else { + $objects = array(array('class' => $class)); + } + $defaults = isset($_this->_config['Model']) ? $_this->_config['Model'] : array(); + $count = count($objects); + + foreach ($objects as $key => $settings) { + if (is_array($settings)) { + $pluginPath = null; + $settings = array_merge($defaults, $settings); + $class = $settings['class']; + + list($plugin, $class) = pluginSplit($class); + if ($plugin) { + $pluginPath = $plugin . '.'; + } + + if (empty($settings['alias'])) { + $settings['alias'] = $class; + } + $alias = $settings['alias']; + + if ($model = $_this->_duplicate($alias, $class)) { + $_this->map($alias, $class); + return $model; + } + + App::uses('Model', 'Model'); + App::uses('AppModel', 'Model'); + App::uses($plugin . 'AppModel', $pluginPath . 'Model'); + App::uses($class, $pluginPath . 'Model'); + + if (class_exists($class)) { + ${$class} = new $class($settings); + if ($strict) { + ${$class} = (${$class} instanceof Model) ? ${$class} : null; + } + } + if (!isset(${$class})) { + if ($strict) { + return false; + } elseif ($plugin && class_exists($plugin . 'AppModel')) { + $appModel = $plugin . 'AppModel'; + } else { + $appModel = 'AppModel'; + } + if (!empty($appModel)) { + $settings['name'] = $class; + ${$class} = new $appModel($settings); + } + + if (!isset(${$class})) { + trigger_error(__d('cake_dev', '(ClassRegistry::init() could not create instance of %1$s class %2$s ', $class, $type), E_USER_WARNING); + return $false; + } + } + $_this->map($alias, $class); + } elseif (is_numeric($settings)) { + trigger_error(__d('cake_dev', '(ClassRegistry::init() Attempted to create instance of a class with a numeric name'), E_USER_WARNING); + return $false; + } + } + + if ($count > 1) { + return $true; + } + return ${$class}; + } + +/** + * Add $object to the registry, associating it with the name $key. + * + * @param string $key Key for the object in registry + * @param mixed $object Object to store + * @return boolean True if the object was written, false if $key already exists + */ + public static function addObject($key, $object) { + $_this = ClassRegistry::getInstance(); + $key = Inflector::underscore($key); + if (!isset($_this->_objects[$key])) { + $_this->_objects[$key] = $object; + return true; + } + return false; + } + +/** + * Remove object which corresponds to given key. + * + * @param string $key Key of object to remove from registry + * @return void + */ + public static function removeObject($key) { + $_this = ClassRegistry::getInstance(); + $key = Inflector::underscore($key); + if (isset($_this->_objects[$key])) { + unset($_this->_objects[$key]); + } + } + +/** + * Returns true if given key is present in the ClassRegistry. + * + * @param string $key Key to look for + * @return boolean true if key exists in registry, false otherwise + */ + public static function isKeySet($key) { + $_this = ClassRegistry::getInstance(); + $key = Inflector::underscore($key); + if (isset($_this->_objects[$key])) { + return true; + } elseif (isset($_this->_map[$key])) { + return true; + } + return false; + } + +/** + * Get all keys from the registry. + * + * @return array Set of keys stored in registry + */ + public static function keys() { + $_this = ClassRegistry::getInstance(); + return array_keys($_this->_objects); + } + +/** + * Return object which corresponds to given key. + * + * @param string $key Key of object to look for + * @return mixed Object stored in registry or boolean false if the object does not exist. + */ + public static function &getObject($key) { + $_this = ClassRegistry::getInstance(); + $key = Inflector::underscore($key); + $return = false; + if (isset($_this->_objects[$key])) { + $return = $_this->_objects[$key]; + } else { + $key = $_this->_getMap($key); + if (isset($_this->_objects[$key])) { + $return = $_this->_objects[$key]; + } + } + return $return; + } + +/** + * Sets the default constructor parameter for an object type + * + * @param string $type Type of object. If this parameter is omitted, defaults to "Model" + * @param array $param The parameter that will be passed to object constructors when objects + * of $type are created + * @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns + * the previously-set value of $param, or null if not set. + */ + public static function config($type, $param = array()) { + $_this = ClassRegistry::getInstance(); + + if (empty($param) && is_array($type)) { + $param = $type; + $type = 'Model'; + } elseif (is_null($param)) { + unset($_this->_config[$type]); + } elseif (empty($param) && is_string($type)) { + return isset($_this->_config[$type]) ? $_this->_config[$type] : null; + } + $_this->_config[$type] = $param; + } + +/** + * Checks to see if $alias is a duplicate $class Object + * + * @param string $alias + * @param string $class + * @return boolean + */ + protected function &_duplicate($alias, $class) { + $duplicate = false; + if ($this->isKeySet($alias)) { + $model = $this->getObject($alias); + if (is_object($model) && (is_a($model, $class) || $model->alias === $class)) { + $duplicate = $model; + } + unset($model); + } + return $duplicate; + } + +/** + * Add a key name pair to the registry to map name to class in the registry. + * + * @param string $key Key to include in map + * @param string $name Key that is being mapped + * @return void + */ + public static function map($key, $name) { + $_this = ClassRegistry::getInstance(); + $key = Inflector::underscore($key); + $name = Inflector::underscore($name); + if (!isset($_this->_map[$key])) { + $_this->_map[$key] = $name; + } + } + +/** + * Get all keys from the map in the registry. + * + * @return array Keys of registry's map + */ + public static function mapKeys() { + $_this = ClassRegistry::getInstance(); + return array_keys($_this->_map); + } + +/** + * Return the name of a class in the registry. + * + * @param string $key Key to find in map + * @return string Mapped value + */ + protected function _getMap($key) { + if (isset($this->_map[$key])) { + return $this->_map[$key]; + } + } + +/** + * Flushes all objects from the ClassRegistry. + * + * @return void + */ + public static function flush() { + $_this = ClassRegistry::getInstance(); + $_this->_objects = array(); + $_this->_map = array(); + } +} diff --git a/app/Cake/Utility/Debugger.php b/app/Cake/Utility/Debugger.php new file mode 100644 index 00000000..64acb4ea --- /dev/null +++ b/app/Cake/Utility/Debugger.php @@ -0,0 +1,720 @@ + array( + 'trace' => '{:reference} - {:path}, line {:line}', + 'error' => "{:error} ({:code}): {:description} in [{:file}, line {:line}]" + ), + 'js' => array( + 'error' => '', + 'info' => '', + 'trace' => '
{:trace}
', + 'code' => '', + 'context' => '', + 'links' => array() + ), + 'html' => array( + 'trace' => '
Trace 

{:trace}

', + 'context' => '
Context 

{:context}

' + ), + 'txt' => array( + 'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}", + 'context' => "Context:\n{:context}\n", + 'trace' => "Trace:\n{:trace}\n", + 'code' => '', + 'info' => '' + ), + 'base' => array( + 'traceLine' => '{:reference} - {:path}, line {:line}' + ), + 'log' => array(), + ); + +/** + * Holds current output data when outputFormat is false. + * + * @var string + */ + protected $_data = array(); + +/** + * Constructor. + * + */ + public function __construct() { + $docRef = ini_get('docref_root'); + + if (empty($docRef) && function_exists('ini_set')) { + ini_set('docref_root', 'http://php.net/'); + } + if (!defined('E_RECOVERABLE_ERROR')) { + define('E_RECOVERABLE_ERROR', 4096); + } + + $e = '
';
+		$e .= '{:error} ({:code}): {:description} ';
+		$e .= '[{:path}, line {:line}]';
+
+		$e .= '';
+		$e .= '
'; + $this->_templates['js']['error'] = $e; + + $t = ''; + $this->_templates['js']['info'] = $t; + + $links = array(); + $link = 'Code'; + $links['code'] = $link; + + $link = 'Context'; + $links['context'] = $link; + + $this->_templates['js']['links'] = $links; + + $this->_templates['js']['context'] = '
_templates['js']['context'] .= 'style="display: none;">{:context}
'; + + $this->_templates['js']['code'] = '
_templates['js']['code'] .= 'style="display: none;">{:code}
'; + + $e = '
{:error} ({:code}) : {:description} ';
+		$e .= '[{:path}, line {:line}]
'; + $this->_templates['html']['error'] = $e; + + $this->_templates['html']['context'] = '
Context ';
+		$this->_templates['html']['context'] .= '

{:context}

'; + } + +/** + * Returns a reference to the Debugger singleton object instance. + * + * @param string $class + * @return object + */ + public static function &getInstance($class = null) { + static $instance = array(); + if (!empty($class)) { + if (!$instance || strtolower($class) != strtolower(get_class($instance[0]))) { + $instance[0] = new $class(); + } + } + if (!$instance) { + $instance[0] = new Debugger(); + } + return $instance[0]; + } + +/** + * Recursively formats and outputs the contents of the supplied variable. + * + * + * @param mixed $var the variable to dump + * @return void + * @see Debugger::exportVar() + * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class + */ + public static function dump($var) { + pr(self::exportVar($var)); + } + +/** + * Creates an entry in the log file. The log entry will contain a stack trace from where it was called. + * as well as export the variable using exportVar. By default the log is written to the debug log. + * + * @param mixed $var Variable or content to log + * @param integer $level type of log to use. Defaults to LOG_DEBUG + * @return void + * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class + */ + public static function log($var, $level = LOG_DEBUG) { + $source = self::trace(array('start' => 1)) . "\n"; + CakeLog::write($level, "\n" . $source . self::exportVar($var)); + } + +/** + * Overrides PHP's default error handling. + * + * @param integer $code Code of error + * @param string $description Error description + * @param string $file File on which error occurred + * @param integer $line Line that triggered the error + * @param array $context Context + * @return boolean true if error was handled + * @deprecated This function is supersceeded by Debugger::outputError() + */ + public static function showError($code, $description, $file = null, $line = null, $context = null) { + $_this = Debugger::getInstance(); + + if (empty($file)) { + $file = '[internal]'; + } + if (empty($line)) { + $line = '??'; + } + $path = self::trimPath($file); + + $info = compact('code', 'description', 'file', 'line'); + if (!in_array($info, $_this->errors)) { + $_this->errors[] = $info; + } else { + return; + } + + switch ($code) { + case E_PARSE: + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + $error = 'Fatal Error'; + $level = LOG_ERROR; + break; + case E_WARNING: + case E_USER_WARNING: + case E_COMPILE_WARNING: + case E_RECOVERABLE_ERROR: + $error = 'Warning'; + $level = LOG_WARNING; + break; + case E_NOTICE: + case E_USER_NOTICE: + $error = 'Notice'; + $level = LOG_NOTICE; + break; + default: + return; + break; + } + + $data = compact( + 'level', 'error', 'code', 'description', 'file', 'path', 'line', 'context' + ); + echo $_this->outputError($data); + + if ($error == 'Fatal Error') { + exit(); + } + return true; + } + +/** + * Outputs a stack trace based on the supplied options. + * + * ### Options + * + * - `depth` - The number of stack frames to return. Defaults to 999 + * - `format` - The format you want the return. Defaults to the currently selected format. If + * format is 'array' or 'points' the return will be an array. + * - `args` - Should arguments for functions be shown? If true, the arguments for each method call + * will be displayed. + * - `start` - The stack frame to start generating a trace from. Defaults to 0 + * + * @param array $options Format for outputting stack trace + * @return mixed Formatted stack trace + * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class + */ + public static function trace($options = array()) { + $_this = Debugger::getInstance(); + $defaults = array( + 'depth' => 999, + 'format' => $_this->_outputFormat, + 'args' => false, + 'start' => 0, + 'scope' => null, + 'exclude' => null + ); + $options += $defaults; + + $backtrace = debug_backtrace(); + $count = count($backtrace); + $back = array(); + + $_trace = array( + 'line' => '??', + 'file' => '[internal]', + 'class' => null, + 'function' => '[main]' + ); + + for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) { + $trace = array_merge(array('file' => '[internal]', 'line' => '??'), $backtrace[$i]); + + if (isset($backtrace[$i + 1])) { + $next = array_merge($_trace, $backtrace[$i + 1]); + $reference = $next['function']; + + if (!empty($next['class'])) { + $reference = $next['class'] . '::' . $reference . '('; + if ($options['args'] && isset($next['args'])) { + $args = array(); + foreach ($next['args'] as $arg) { + $args[] = Debugger::exportVar($arg); + } + $reference .= join(', ', $args); + } + $reference .= ')'; + } + } else { + $reference = '[main]'; + } + if (in_array($reference, array('call_user_func_array', 'trigger_error'))) { + continue; + } + if ($options['format'] == 'points' && $trace['file'] != '[internal]') { + $back[] = array('file' => $trace['file'], 'line' => $trace['line']); + } elseif ($options['format'] == 'array') { + $back[] = $trace; + } else { + if (isset($_this->_templates[$options['format']]['traceLine'])) { + $tpl = $_this->_templates[$options['format']]['traceLine']; + } else { + $tpl = $_this->_templates['base']['traceLine']; + } + $trace['path'] = self::trimPath($trace['file']); + $trace['reference'] = $reference; + unset($trace['object'], $trace['args']); + $back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}')); + } + } + + if ($options['format'] == 'array' || $options['format'] == 'points') { + return $back; + } + return implode("\n", $back); + } + +/** + * Shortens file paths by replacing the application base path with 'APP', and the CakePHP core + * path with 'CORE'. + * + * @param string $path Path to shorten + * @return string Normalized path + */ + public static function trimPath($path) { + if (!defined('CAKE_CORE_INCLUDE_PATH') || !defined('APP')) { + return $path; + } + + if (strpos($path, APP) === 0) { + return str_replace(APP, 'APP' . DS, $path); + } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) { + return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path); + } elseif (strpos($path, ROOT) === 0) { + return str_replace(ROOT, 'ROOT', $path); + } + + if (strpos($path, CAKE) === 0) { + return str_replace($corePath, 'CORE' . DS, $path); + } + return $path; + } + +/** + * Grabs an excerpt from a file and highlights a given line of code. + * + * Usage: + * + * `Debugger::excerpt('/path/to/file', 100, 4);` + * + * The above would return an array of 8 items. The 4th item would be the provided line, + * and would be wrapped in ``. All of the lines + * are processed with highlight_string() as well, so they have basic PHP syntax highlighting + * applied. + * + * @param string $file Absolute path to a PHP file + * @param integer $line Line number to highlight + * @param integer $context Number of lines of context to extract above and below $line + * @return array Set of lines highlighted + * @see http://php.net/highlight_string + * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class + */ + public static function excerpt($file, $line, $context = 2) { + $lines = array(); + if (!file_exists($file)) { + return array(); + } + $data = @explode("\n", file_get_contents($file)); + + if (empty($data) || !isset($data[$line])) { + return; + } + for ($i = $line - ($context + 1); $i < $line + $context; $i++) { + if (!isset($data[$i])) { + continue; + } + $string = str_replace(array("\r\n", "\n"), "", highlight_string($data[$i], true)); + if ($i == $line) { + $lines[] = '' . $string . ''; + } else { + $lines[] = $string; + } + } + return $lines; + } + +/** + * Converts a variable to a string for debug output. + * + * *Note:* The following keys will have their contents replaced with + * `*****`: + * + * - password + * - login + * - host + * - database + * - port + * - prefix + * - schema + * + * This is done to protect database credentials, which could be accidentally + * shown in an error message if CakePHP is deployed in development mode. + * + * @param string $var Variable to convert + * @param integer $recursion + * @return string Variable as a formatted string + * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class + */ + public static function exportVar($var, $recursion = 0) { + switch (strtolower(gettype($var))) { + case 'boolean': + return ($var) ? 'true' : 'false'; + break; + case 'integer': + case 'double': + return $var; + break; + case 'string': + if (trim($var) == "") { + return '""'; + } + return '"' . h($var) . '"'; + break; + case 'object': + return get_class($var) . "\n" . self::_object($var); + case 'array': + $var = array_merge($var, array_intersect_key(array( + 'password' => '*****', + 'login' => '*****', + 'host' => '*****', + 'database' => '*****', + 'port' => '*****', + 'prefix' => '*****', + 'schema' => '*****' + ), $var)); + + $out = "array("; + $vars = array(); + foreach ($var as $key => $val) { + if ($recursion >= 0) { + if (is_numeric($key)) { + $vars[] = "\n\t" . self::exportVar($val, $recursion - 1); + } else { + $vars[] = "\n\t" . self::exportVar($key, $recursion - 1) + . ' => ' . self::exportVar($val, $recursion - 1); + } + } + } + $n = null; + if (!empty($vars)) { + $n = "\n"; + } + return $out . implode(",", $vars) . "{$n})"; + break; + case 'resource': + return strtolower(gettype($var)); + break; + case 'null': + return 'null'; + break; + } + } + +/** + * Handles object to string conversion. + * + * @param string $var Object to convert + * @return string + * @see Debugger::exportVar() + */ + protected static function _object($var) { + $out = array(); + + if (is_object($var)) { + $className = get_class($var); + $objectVars = get_object_vars($var); + + foreach ($objectVars as $key => $value) { + if (is_object($value)) { + $value = get_class($value) . ' object'; + } elseif (is_array($value)) { + $value = 'array'; + } elseif ($value === null) { + $value = 'NULL'; + } elseif (in_array(gettype($value), array('boolean', 'integer', 'double', 'string', 'array', 'resource'))) { + $value = Debugger::exportVar($value); + } + $out[] = "$className::$$key = " . $value; + } + } + return implode("\n", $out); + } + +/** + * Get/Set the output format for Debugger error rendering. + * + * @param string $format The format you want errors to be output as. + * Leave null to get the current format. + * @return mixed Returns null when setting. Returns the current format when getting. + * @throws CakeException when choosing a format that doesn't exist. + */ + public static function outputAs($format = null) { + $self = Debugger::getInstance(); + if ($format === null) { + return $self->_outputFormat; + } + if ($format !== false && !isset($self->_templates[$format])) { + throw new CakeException(__d('cake_dev', 'Invalid Debugger output format.')); + } + $self->_outputFormat = $format; + } + +/** + * Add an output format or update a format in Debugger. + * + * `Debugger::addFormat('custom', $data);` + * + * Where $data is an array of strings that use String::insert() variable + * replacement. The template vars should be in a `{:id}` style. + * An error formatter can have the following keys: + * + * - 'error' - Used for the container for the error message. Gets the following template + * variables: `id`, `error`, `code`, `description`, `path`, `line`, `links`, `info` + * - 'info' - A combination of `code`, `context` and `trace`. Will be set with + * the contents of the other template keys. + * - 'trace' - The container for a stack trace. Gets the following template + * variables: `trace` + * - 'context' - The container element for the context variables. + * Gets the following templates: `id`, `context` + * - 'links' - An array of HTML links that are used for creating links to other resources. + * Typically this is used to create javascript links to open other sections. + * Link keys, are: `code`, `context`, `help`. See the js output format for an + * example. + * - 'traceLine' - Used for creating lines in the stacktrace. Gets the following + * template variables: `reference`, `path`, `line` + * + * Alternatively if you want to use a custom callback to do all the formatting, you can use + * the callback key, and provide a callable: + * + * `Debugger::addFormat('custom', array('callback' => array($foo, 'outputError'));` + * + * The callback can expect two parameters. The first is an array of all + * the error data. The second contains the formatted strings generated using + * the other template strings. Keys like `info`, `links`, `code`, `context` and `trace` + * will be present depending on the other templates in the format type. + * + * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for + * straight HTML output, or 'txt' for unformatted text. + * @param array $strings Template strings, or a callback to be used for the output format. + * @return The resulting format string set. + */ + public static function addFormat($format, array $strings) { + $self = Debugger::getInstance(); + if (isset($self->_templates[$format])) { + if (isset($strings['links'])) { + $self->_templates[$format]['links'] = array_merge( + $self->_templates[$format]['links'], + $strings['links'] + ); + unset($strings['links']); + } + $self->_templates[$format] = array_merge($self->_templates[$format], $strings); + } else { + $self->_templates[$format] = $strings; + } + return $self->_templates[$format]; + } + +/** + * Switches output format, updates format strings. + * Can be used to switch the active output format: + * + * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for + * straight HTML output, or 'txt' for unformatted text. + * @param array $strings Template strings to be used for the output format. + * @return string + * @deprecated Use Debugger::outputFormat() and Debugger::addFormat(). Will be removed + * in 3.0 + */ + public function output($format = null, $strings = array()) { + $_this = Debugger::getInstance(); + $data = null; + + if (is_null($format)) { + return Debugger::outputAs(); + } + + if (!empty($strings)) { + return Debugger::addFormat($format, $strings); + } + + if ($format === true && !empty($_this->_data)) { + $data = $_this->_data; + $_this->_data = array(); + $format = false; + } + Debugger::outputAs($format); + return $data; + } + +/** + * Takes a processed array of data from an error and displays it in the chosen format. + * + * @param string $data + * @return void + */ + public function outputError($data) { + $defaults = array( + 'level' => 0, + 'error' => 0, + 'code' => 0, + 'description' => '', + 'file' => '', + 'line' => 0, + 'context' => array(), + 'start' => 2, + ); + $data += $defaults; + + $files = $this->trace(array('start' => $data['start'], 'format' => 'points')); + $code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1); + $trace = $this->trace(array('start' => $data['start'], 'depth' => '20')); + $insertOpts = array('before' => '{:', 'after' => '}'); + $context = array(); + $links = array(); + $info = ''; + + foreach ((array)$data['context'] as $var => $value) { + $context[] = "\${$var}\t=\t" . $this->exportVar($value, 1); + } + + switch ($this->_outputFormat) { + case false: + $this->_data[] = compact('context', 'trace') + $data; + return; + case 'log': + $this->log(compact('context', 'trace') + $data); + return; + } + + $data['id'] = 'cakeErr' . uniqid(); + $tpl = array_merge($this->_templates['base'], $this->_templates[$this->_outputFormat]); + $insert = array('context' => join("\n", $context)) + $data; + + $detect = array('context'); + + if (isset($tpl['links'])) { + foreach ($tpl['links'] as $key => $val) { + if (in_array($key, $detect) && empty($insert[$key])) { + continue; + } + $links[$key] = String::insert($val, $insert, $insertOpts); + } + } + + foreach (array('code', 'context', 'trace') as $key) { + if (empty($$key) || !isset($tpl[$key])) { + continue; + } + if (is_array($$key)) { + $$key = join("\n", $$key); + } + $info .= String::insert($tpl[$key], compact($key) + $insert, $insertOpts); + } + $links = join(' ', $links); + unset($data['context']); + if (isset($tpl['callback']) && is_callable($tpl['callback'])) { + return call_user_func($tpl['callback'], $data, compact('links', 'info')); + } + echo String::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts); + } + +/** + * Verifies that the application's salt and cipher seed value has been changed from the default value. + * + * @return void + */ + public static function checkSecurityKeys() { + if (Configure::read('Security.salt') == 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') { + trigger_error(__d('cake_dev', 'Please change the value of \'Security.salt\' in app/Config/core.php to a salt value specific to your application'), E_USER_NOTICE); + } + + if (Configure::read('Security.cipherSeed') === '76859309657453542496749683645') { + trigger_error(__d('cake_dev', 'Please change the value of \'Security.cipherSeed\' in app/Config/core.php to a numeric (digits only) seed value specific to your application'), E_USER_NOTICE); + } + } + +} diff --git a/app/Cake/Utility/File.php b/app/Cake/Utility/File.php new file mode 100644 index 00000000..a2268786 --- /dev/null +++ b/app/Cake/Utility/File.php @@ -0,0 +1,506 @@ +Folder = new Folder(dirname($path), $create, $mode); + if (!is_dir($path)) { + $this->name = basename($path); + } + $this->pwd(); + $create && !$this->exists() && $this->safe($path) && $this->create(); + } + +/** + * Closes the current file if it is opened + * + */ + public function __destruct() { + $this->close(); + } + +/** + * Creates the File. + * + * @return boolean Success + */ + public function create() { + $dir = $this->Folder->pwd(); + if (is_dir($dir) && is_writable($dir) && !$this->exists()) { + $old = umask(0); + if (touch($this->path)) { + umask($old); + return true; + } + } + return false; + } + +/** + * Opens the current file with a given $mode + * + * @param string $mode A valid 'fopen' mode string (r|w|a ...) + * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't + * @return boolean True on success, false on failure + */ + public function open($mode = 'r', $force = false) { + if (!$force && is_resource($this->handle)) { + return true; + } + clearstatcache(); + if ($this->exists() === false) { + if ($this->create() === false) { + return false; + } + } + + $this->handle = fopen($this->path, $mode); + if (is_resource($this->handle)) { + return true; + } + return false; + } + +/** + * Return the contents of this File as a string. + * + * @param string $bytes where to start + * @param string $mode A `fread` compatible mode. + * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't + * @return mixed string on success, false on failure + */ + public function read($bytes = false, $mode = 'rb', $force = false) { + if ($bytes === false && $this->lock === null) { + return file_get_contents($this->path); + } + if ($this->open($mode, $force) === false) { + return false; + } + if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) { + return false; + } + if (is_int($bytes)) { + return fread($this->handle, $bytes); + } + + $data = ''; + while (!feof($this->handle)) { + $data .= fgets($this->handle, 4096); + } + + if ($this->lock !== null) { + flock($this->handle, LOCK_UN); + } + if ($bytes === false) { + $this->close(); + } + return trim($data); + } + +/** + * Sets or gets the offset for the currently opened file. + * + * @param mixed $offset The $offset in bytes to seek. If set to false then the current offset is returned. + * @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to + * @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode) + */ + public function offset($offset = false, $seek = SEEK_SET) { + if ($offset === false) { + if (is_resource($this->handle)) { + return ftell($this->handle); + } + } elseif ($this->open() === true) { + return fseek($this->handle, $offset, $seek) === 0; + } + return false; + } + +/** + * Prepares a ascii string for writing. Converts line endings to the + * correct terminator for the current platform. If windows "\r\n" will be used + * all other platforms will use "\n" + * + * @param string $data Data to prepare for writing. + * @param boolean $forceWindows + * @return string The with converted line endings. + */ + public static function prepare($data, $forceWindows = false) { + $lineBreak = "\n"; + if (DIRECTORY_SEPARATOR == '\\' || $forceWindows === true) { + $lineBreak = "\r\n"; + } + return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak)); + } + +/** + * Write given data to this File. + * + * @param string $data Data to write to this File. + * @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}. + * @param string $force force the file to open + * @return boolean Success + */ + public function write($data, $mode = 'w', $force = false) { + $success = false; + if ($this->open($mode, $force) === true) { + if ($this->lock !== null) { + if (flock($this->handle, LOCK_EX) === false) { + return false; + } + } + + if (fwrite($this->handle, $data) !== false) { + $success = true; + } + if ($this->lock !== null) { + flock($this->handle, LOCK_UN); + } + } + return $success; + } + +/** + * Append given data string to this File. + * + * @param string $data Data to write + * @param string $force force the file to open + * @return boolean Success + */ + public function append($data, $force = false) { + return $this->write($data, 'a', $force); + } + +/** + * Closes the current file if it is opened. + * + * @return boolean True if closing was successful or file was already closed, otherwise false + */ + public function close() { + if (!is_resource($this->handle)) { + return true; + } + return fclose($this->handle); + } + +/** + * Deletes the File. + * + * @return boolean Success + */ + public function delete() { + clearstatcache(); + if (is_resource($this->handle)) { + fclose($this->handle); + $this->handle = null; + } + if ($this->exists()) { + return unlink($this->path); + } + return false; + } + +/** + * Returns the File info. + * + * @return string The File extension + */ + public function info() { + if ($this->info == null) { + $this->info = pathinfo($this->path); + } + if (!isset($this->info['filename'])) { + $this->info['filename'] = $this->name(); + } + return $this->info; + } + +/** + * Returns the File extension. + * + * @return string The File extension + */ + public function ext() { + if ($this->info == null) { + $this->info(); + } + if (isset($this->info['extension'])) { + return $this->info['extension']; + } + return false; + } + +/** + * Returns the File name without extension. + * + * @return string The File name without extension. + */ + public function name() { + if ($this->info == null) { + $this->info(); + } + if (isset($this->info['extension'])) { + return basename($this->name, '.'.$this->info['extension']); + } elseif ($this->name) { + return $this->name; + } + return false; + } + +/** + * makes filename safe for saving + * + * @param string $name The name of the file to make safe if different from $this->name + * @param string $ext The name of the extension to make safe if different from $this->ext + * @return string $ext the extension of the file + */ + public function safe($name = null, $ext = null) { + if (!$name) { + $name = $this->name; + } + if (!$ext) { + $ext = $this->ext(); + } + return preg_replace( "/(?:[^\w\.-]+)/", "_", basename($name, $ext)); + } + +/** + * Get md5 Checksum of file with previous check of Filesize + * + * @param mixed $maxsize in MB or true to force + * @return string md5 Checksum {@link http://php.net/md5_file See md5_file()} + */ + public function md5($maxsize = 5) { + if ($maxsize === true) { + return md5_file($this->path); + } + + $size = $this->size(); + if ($size && $size < ($maxsize * 1024) * 1024) { + return md5_file($this->path); + } + + return false; + } + +/** + * Returns the full path of the File. + * + * @return string Full path to file + */ + public function pwd() { + if (is_null($this->path)) { + $this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name; + } + return $this->path; + } + +/** + * Returns true if the File exists. + * + * @return boolean true if it exists, false otherwise + */ + public function exists() { + return (file_exists($this->path) && is_file($this->path)); + } + +/** + * Returns the "chmod" (permissions) of the File. + * + * @return string Permissions for the file + */ + public function perms() { + if ($this->exists()) { + return substr(sprintf('%o', fileperms($this->path)), -4); + } + return false; + } + +/** + * Returns the Filesize + * + * @return integer size of the file in bytes, or false in case of an error + */ + public function size() { + if ($this->exists()) { + return filesize($this->path); + } + return false; + } + +/** + * Returns true if the File is writable. + * + * @return boolean true if its writable, false otherwise + */ + public function writable() { + return is_writable($this->path); + } + +/** + * Returns true if the File is executable. + * + * @return boolean true if its executable, false otherwise + */ + public function executable() { + return is_executable($this->path); + } + +/** + * Returns true if the File is readable. + * + * @return boolean true if file is readable, false otherwise + */ + public function readable() { + return is_readable($this->path); + } + +/** + * Returns the File's owner. + * + * @return integer the Fileowner + */ + public function owner() { + if ($this->exists()) { + return fileowner($this->path); + } + return false; + } + +/** + * Returns the File's group. + * + * @return integer the Filegroup + */ + public function group() { + if ($this->exists()) { + return filegroup($this->path); + } + return false; + } + +/** + * Returns last access time. + * + * @return integer timestamp Timestamp of last access time + */ + public function lastAccess() { + if ($this->exists()) { + return fileatime($this->path); + } + return false; + } + +/** + * Returns last modified time. + * + * @return integer timestamp Timestamp of last modification + */ + public function lastChange() { + if ($this->exists()) { + return filemtime($this->path); + } + return false; + } + +/** + * Returns the current folder. + * + * @return Folder Current folder + */ + public function &Folder() { + return $this->Folder; + } + +/** + * Copy the File to $dest + * + * @param string $dest destination for the copy + * @param boolean $overwrite Overwrite $dest if exists + * @return boolean Succes + */ + public function copy($dest, $overwrite = true) { + if (!$this->exists() || is_file($dest) && !$overwrite) { + return false; + } + return copy($this->path, $dest); + } +} diff --git a/app/Cake/Utility/Folder.php b/app/Cake/Utility/Folder.php new file mode 100644 index 00000000..671ff1e2 --- /dev/null +++ b/app/Cake/Utility/Folder.php @@ -0,0 +1,755 @@ +mode = $mode; + } + + if (!file_exists($path) && $create === true) { + $this->create($path, $this->mode); + } + if (!Folder::isAbsolute($path)) { + $path = realpath($path); + } + if (!empty($path)) { + $this->cd($path); + } + } + +/** + * Return current path. + * + * @return string Current path + */ + public function pwd() { + return $this->path; + } + +/** + * Change directory to $path. + * + * @param string $path Path to the directory to change to + * @return string The new path. Returns false on failure + */ + public function cd($path) { + $path = $this->realpath($path); + if (is_dir($path)) { + return $this->path = $path; + } + return false; + } + +/** + * Returns an array of the contents of the current directory. + * The returned array holds two arrays: One of directories and one of files. + * + * @param boolean $sort Whether you want the results sorted, set this and the sort property + * to false to get unsorted results. + * @param mixed $exceptions Either an array or boolean true will not grab dot files + * @param boolean $fullPath True returns the full path + * @return mixed Contents of current directory as an array, an empty array on failure + */ + public function read($sort = true, $exceptions = false, $fullPath = false) { + $dirs = $files = array(); + + if (!$this->pwd()) { + return array($dirs, $files); + } + if (is_array($exceptions)) { + $exceptions = array_flip($exceptions); + } + $skipHidden = isset($exceptions['.']) || $exceptions === true; + + try { + $iterator = new DirectoryIterator($this->path); + } catch (UnexpectedValueException $e) { + return array($dirs, $files); + } + + foreach ($iterator as $item) { + if ($item->isDot()) { + continue; + } + $name = $item->getFileName(); + if ($skipHidden && $name[0] === '.' || isset($exceptions[$name])) { + continue; + } + if ($fullPath) { + $name = $item->getPathName(); + } + if ($item->isDir()) { + $dirs[] = $name; + } else { + $files[] = $name; + } + } + if ($sort || $this->sort) { + sort($dirs); + sort($files); + } + return array($dirs, $files); + } + +/** + * Returns an array of all matching files in current directory. + * + * @param string $regexpPattern Preg_match pattern (Defaults to: .*) + * @param boolean $sort Whether results should be sorted. + * @return array Files that match given pattern + */ + public function find($regexpPattern = '.*', $sort = false) { + list($dirs, $files) = $this->read($sort); + return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files)); ; + } + +/** + * Returns an array of all matching files in and below current directory. + * + * @param string $pattern Preg_match pattern (Defaults to: .*) + * @param boolean $sort Whether results should be sorted. + * @return array Files matching $pattern + */ + public function findRecursive($pattern = '.*', $sort = false) { + if (!$this->pwd()) { + return array(); + } + $startsOn = $this->path; + $out = $this->_findRecursive($pattern, $sort); + $this->cd($startsOn); + return $out; + } + +/** + * Private helper function for findRecursive. + * + * @param string $pattern Pattern to match against + * @param boolean $sort Whether results should be sorted. + * @return array Files matching pattern + */ + protected function _findRecursive($pattern, $sort = false) { + list($dirs, $files) = $this->read($sort); + $found = array(); + + foreach ($files as $file) { + if (preg_match('/^' . $pattern . '$/i', $file)) { + $found[] = Folder::addPathElement($this->path, $file); + } + } + $start = $this->path; + + foreach ($dirs as $dir) { + $this->cd(Folder::addPathElement($start, $dir)); + $found = array_merge($found, $this->findRecursive($pattern, $sort)); + } + return $found; + } + +/** + * Returns true if given $path is a Windows path. + * + * @param string $path Path to check + * @return boolean true if windows path, false otherwise + */ + public static function isWindowsPath($path) { + return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\'); + } + +/** + * Returns true if given $path is an absolute path. + * + * @param string $path Path to check + * @return boolean true if path is absolute. + */ + public static function isAbsolute($path) { + return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\'); + } + +/** + * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.) + * + * @param string $path Path to check + * @return string Set of slashes ("\\" or "/") + */ + public static function normalizePath($path) { + return Folder::correctSlashFor($path); + } + +/** + * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.) + * + * @param string $path Path to check + * @return string Set of slashes ("\\" or "/") + */ + public static function correctSlashFor($path) { + return (Folder::isWindowsPath($path)) ? '\\' : '/'; + } + +/** + * Returns $path with added terminating slash (corrected for Windows or other OS). + * + * @param string $path Path to check + * @return string Path with ending slash + */ + public static function slashTerm($path) { + if (Folder::isSlashTerm($path)) { + return $path; + } + return $path . Folder::correctSlashFor($path); + } + +/** + * Returns $path with $element added, with correct slash in-between. + * + * @param string $path Path + * @param string $element Element to and at end of path + * @return string Combined path + */ + public static function addPathElement($path, $element) { + return rtrim($path, DS) . DS . $element; + } + +/** + * Returns true if the File is in a given CakePath. + * + * @param string $path The path to check. + * @return boolean + */ + public function inCakePath($path = '') { + $dir = substr(Folder::slashTerm(ROOT), 0, -1); + $newdir = $dir . $path; + + return $this->inPath($newdir); + } + +/** + * Returns true if the File is in given path. + * + * @param string $path The path to check that the current pwd() resides with in. + * @param boolean $reverse + * @return boolean + */ + public function inPath($path = '', $reverse = false) { + $dir = Folder::slashTerm($path); + $current = Folder::slashTerm($this->pwd()); + + if (!$reverse) { + $return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current); + } else { + $return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir); + } + return (bool)$return; + } + +/** + * Change the mode on a directory structure recursively. This includes changing the mode on files as well. + * + * @param string $path The path to chmod + * @param integer $mode octal value 0755 + * @param boolean $recursive chmod recursively, set to false to only change the current directory. + * @param array $exceptions array of files, directories to skip + * @return boolean Returns TRUE on success, FALSE on failure + */ + public function chmod($path, $mode = false, $recursive = true, $exceptions = array()) { + if (!$mode) { + $mode = $this->mode; + } + + if ($recursive === false && is_dir($path)) { + if (@chmod($path, intval($mode, 8))) { + $this->_messages[] = __d('cake_dev', '%s changed to %s', $path, $mode); + return true; + } + + $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $path, $mode); + return false; + } + + if (is_dir($path)) { + $paths = $this->tree($path); + + foreach ($paths as $type) { + foreach ($type as $key => $fullpath) { + $check = explode(DS, $fullpath); + $count = count($check); + + if (in_array($check[$count - 1], $exceptions)) { + continue; + } + + if (@chmod($fullpath, intval($mode, 8))) { + $this->_messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode); + } else { + $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode); + } + } + } + + if (empty($this->_errors)) { + return true; + } + } + return false; + } + +/** + * Returns an array of nested directories and files in each directory + * + * @param string $path the directory path to build the tree from + * @param mixed $exceptions Array of files to exclude, defaults to excluding hidden files. + * @param string $type either file or dir. null returns both files and directories + * @return mixed array of nested directories and files in each directory + */ + public function tree($path = null, $exceptions = true, $type = null) { + if ($path == null) { + $path = $this->path; + } + $files = array(); + $directories = array($path); + $skipHidden = false; + + if ($exceptions === false) { + $skipHidden = true; + } + if (is_array($exceptions)) { + $exceptions = array_flip($exceptions); + } + + try { + $directory = new RecursiveDirectoryIterator($path); + $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST); + } catch (UnexpectedValueException $e) { + if ($type === null) { + return array(array(), array()); + } + return array(); + } + foreach ($iterator as $item) { + $name = $item->getFileName(); + if ($skipHidden && $name[0] === '.' || isset($exceptions[$name])) { + continue; + } + if ($item->isFile()) { + $files[] = $item->getPathName(); + } else if ($item->isDir()) { + $directories[] = $item->getPathName(); + } + } + if ($type === null) { + return array($directories, $files); + } + if ($type === 'dir') { + return $directories; + } + return $files; + } + +/** + * Private method to list directories and files in each directory + * + * @param string $path The Path to read. + * @param mixed $exceptions Array of files to exclude from the read that will be performed. + * @return void + */ + protected function _tree($path, $exceptions) { + $this->path = $path; + list($dirs, $files) = $this->read(false, $exceptions, true); + $this->_directories = array_merge($this->_directories, $dirs); + $this->_files = array_merge($this->_files, $files); + } + +/** + * Create a directory structure recursively. Can be used to create + * deep path structures like `/foo/bar/baz/shoe/horn` + * + * @param string $pathname The directory structure to create + * @param integer $mode octal value 0755 + * @return boolean Returns TRUE on success, FALSE on failure + */ + public function create($pathname, $mode = false) { + if (is_dir($pathname) || empty($pathname)) { + return true; + } + + if (!$mode) { + $mode = $this->mode; + } + + if (is_file($pathname)) { + $this->_errors[] = __d('cake_dev', '%s is a file', $pathname); + return false; + } + $pathname = rtrim($pathname, DS); + $nextPathname = substr($pathname, 0, strrpos($pathname, DS)); + + if ($this->create($nextPathname, $mode)) { + if (!file_exists($pathname)) { + $old = umask(0); + if (mkdir($pathname, $mode)) { + umask($old); + $this->_messages[] = __d('cake_dev', '%s created', $pathname); + return true; + } else { + umask($old); + $this->_errors[] = __d('cake_dev', '%s NOT created', $pathname); + return false; + } + } + } + return false; + } + +/** + * Returns the size in bytes of this Folder and its contents. + * + * @return integer size in bytes of current folder + */ + public function dirsize() { + $size = 0; + $directory = Folder::slashTerm($this->path); + $stack = array($directory); + $count = count($stack); + for ($i = 0, $j = $count; $i < $j; ++$i) { + if (is_file($stack[$i])) { + $size += filesize($stack[$i]); + } elseif (is_dir($stack[$i])) { + $dir = dir($stack[$i]); + if ($dir) { + while (false !== ($entry = $dir->read())) { + if ($entry === '.' || $entry === '..') { + continue; + } + $add = $stack[$i] . $entry; + + if (is_dir($stack[$i] . $entry)) { + $add = Folder::slashTerm($add); + } + $stack[] = $add; + } + $dir->close(); + } + } + $j = count($stack); + } + return $size; + } + +/** + * Recursively Remove directories if the system allows. + * + * @param string $path Path of directory to delete + * @return boolean Success + */ + public function delete($path = null) { + if (!$path) { + $path = $this->pwd(); + } + if (!$path) { + return null; + } + $path = Folder::slashTerm($path); + if (is_dir($path) === true) { + $normalFiles = glob($path . '*'); + $hiddenFiles = glob($path . '\.?*'); + + $normalFiles = $normalFiles ? $normalFiles : array(); + $hiddenFiles = $hiddenFiles ? $hiddenFiles : array(); + + $files = array_merge($normalFiles, $hiddenFiles); + if (is_array($files)) { + foreach ($files as $file) { + if (preg_match('/(\.|\.\.)$/', $file)) { + continue; + } + if (is_file($file) === true) { + if (@unlink($file)) { + $this->_messages[] = __d('cake_dev', '%s removed', $file); + } else { + $this->_errors[] = __d('cake_dev', '%s NOT removed', $file); + } + } elseif (is_dir($file) === true && $this->delete($file) === false) { + return false; + } + } + } + $path = substr($path, 0, strlen($path) - 1); + if (rmdir($path) === false) { + $this->_errors[] = __d('cake_dev', '%s NOT removed', $path); + return false; + } else { + $this->_messages[] = __d('cake_dev', '%s removed', $path); + } + } + return true; + } + +/** + * Recursive directory copy. + * + * ### Options + * + * - `to` The directory to copy to. + * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd(). + * - `mode` The mode to copy the files/directories with. + * - `skip` Files/directories to skip. + * + * @param mixed $options Either an array of options (see above) or a string of the destination directory. + * @return boolean Success + */ + public function copy($options = array()) { + if (!$this->pwd()) { + return false; + } + $to = null; + if (is_string($options)) { + $to = $options; + $options = array(); + } + $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options); + + $fromDir = $options['from']; + $toDir = $options['to']; + $mode = $options['mode']; + + if (!$this->cd($fromDir)) { + $this->_errors[] = __d('cake_dev', '%s not found', $fromDir); + return false; + } + + if (!is_dir($toDir)) { + $this->create($toDir, $mode); + } + + if (!is_writable($toDir)) { + $this->_errors[] = __d('cake_dev', '%s not writable', $toDir); + return false; + } + + $exceptions = array_merge(array('.', '..', '.svn'), $options['skip']); + if ($handle = @opendir($fromDir)) { + while (false !== ($item = readdir($handle))) { + if (!in_array($item, $exceptions)) { + $from = Folder::addPathElement($fromDir, $item); + $to = Folder::addPathElement($toDir, $item); + if (is_file($from)) { + if (copy($from, $to)) { + chmod($to, intval($mode, 8)); + touch($to, filemtime($from)); + $this->_messages[] = __d('cake_dev', '%s copied to %s', $from, $to); + } else { + $this->_errors[] = __d('cake_dev', '%s NOT copied to %s', $from, $to); + } + } + + if (is_dir($from) && !file_exists($to)) { + $old = umask(0); + if (mkdir($to, $mode)) { + umask($old); + $old = umask(0); + chmod($to, $mode); + umask($old); + $this->_messages[] = __d('cake_dev', '%s created', $to); + $options = array_merge($options, array('to'=> $to, 'from'=> $from)); + $this->copy($options); + } else { + $this->_errors[] = __d('cake_dev', '%s not created', $to); + } + } + } + } + closedir($handle); + } else { + return false; + } + + if (!empty($this->_errors)) { + return false; + } + return true; + } + +/** + * Recursive directory move. + * + * ### Options + * + * - `to` The directory to copy to. + * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd(). + * - `chmod` The mode to copy the files/directories with. + * - `skip` Files/directories to skip. + * + * @param array $options (to, from, chmod, skip) + * @return boolean Success + */ + public function move($options) { + $to = null; + if (is_string($options)) { + $to = $options; + $options = (array)$options; + } + $options = array_merge( + array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), + $options + ); + + if ($this->copy($options)) { + if ($this->delete($options['from'])) { + return (bool)$this->cd($options['to']); + } + } + return false; + } + +/** + * get messages from latest method + * + * @return array + */ + public function messages() { + return $this->_messages; + } + +/** + * get error from latest method + * + * @return array + */ + public function errors() { + return $this->_errors; + } + +/** + * Get the real path (taking ".." and such into account) + * + * @param string $path Path to resolve + * @return string The resolved path + */ + public function realpath($path) { + $path = str_replace('/', DS, trim($path)); + if (strpos($path, '..') === false) { + if (!Folder::isAbsolute($path)) { + $path = Folder::addPathElement($this->path, $path); + } + return $path; + } + $parts = explode(DS, $path); + $newparts = array(); + $newpath = ''; + if ($path[0] === DS) { + $newpath = DS; + } + + while (($part = array_shift($parts)) !== NULL) { + if ($part === '.' || $part === '') { + continue; + } + if ($part === '..') { + if (!empty($newparts)) { + array_pop($newparts); + continue; + } else { + return false; + } + } + $newparts[] = $part; + } + $newpath .= implode(DS, $newparts); + + return Folder::slashTerm($newpath); + } + +/** + * Returns true if given $path ends in a slash (i.e. is slash-terminated). + * + * @param string $path Path to check + * @return boolean true if path ends with slash, false otherwise + */ + public static function isSlashTerm($path) { + $lastChar = $path[strlen($path) - 1]; + return $lastChar === '/' || $lastChar === '\\'; + } +} diff --git a/app/Cake/Utility/Inflector.php b/app/Cake/Utility/Inflector.php new file mode 100644 index 00000000..736ac9dd --- /dev/null +++ b/app/Cake/Utility/Inflector.php @@ -0,0 +1,553 @@ + array( + '/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people' + ), + 'irregular' => array( + 'atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'child' => 'children', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs' + ) + ); + +/** + * Singular inflector rules + * + * @var array + */ + protected static $_singular = array( + 'rules' => array( + '/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|ba|diagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '' + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss' + ), + 'irregular' => array( + 'waves' => 'wave', + 'curves' => 'curve' + ) + ); + +/** + * Words that should not be inflected + * + * @var array + */ + protected static $_uninflected = array( + 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', + 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', + 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder', + 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', + 'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', + 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', + 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', + 'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', + 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes', + 'trousers', 'trout','tuna', 'Vermontese', 'Wenchowese', 'whiting', 'wildebeest', + 'Yengeese' + ); + +/** + * Default map of accented and special characters to ASCII characters + * + * @var array + */ + protected static $_transliteration = array( + '/ä|æ|ǽ/' => 'ae', + '/ö|Å“/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/À|Ã|Â|Ã|Ä|Ã…|Ǻ|Ä€|Ä‚|Ä„|Ç/' => 'A', + '/à|á|â|ã|Ã¥|Ç»|Ä|ă|Ä…|ÇŽ|ª/' => 'a', + '/Ç|Ć|Ĉ|ÄŠ|ÄŒ/' => 'C', + '/ç|ć|ĉ|Ä‹|Ä/' => 'c', + '/Ã|ÄŽ|Ä/' => 'D', + '/ð|Ä|Ä‘/' => 'd', + '/È|É|Ê|Ë|Ä’|Ä”|Ä–|Ę|Äš/' => 'E', + '/è|é|ê|ë|Ä“|Ä•|Ä—|Ä™|Ä›/' => 'e', + '/Äœ|Äž|Ä |Ä¢/' => 'G', + '/Ä|ÄŸ|Ä¡|Ä£/' => 'g', + '/Ĥ|Ħ/' => 'H', + '/Ä¥|ħ/' => 'h', + '/ÃŒ|Ã|ÃŽ|Ã|Ĩ|Ī|Ĭ|Ç|Ä®|İ/' => 'I', + '/ì|í|î|ï|Ä©|Ä«|Ä­|Ç|į|ı/' => 'i', + '/Ä´/' => 'J', + '/ĵ/' => 'j', + '/Ķ/' => 'K', + '/Ä·/' => 'k', + '/Ĺ|Ä»|Ľ|Ä¿|Å/' => 'L', + '/ĺ|ļ|ľ|Å€|Å‚/' => 'l', + '/Ñ|Ń|Å…|Ň/' => 'N', + '/ñ|Å„|ņ|ň|ʼn/' => 'n', + '/Ã’|Ó|Ô|Õ|ÅŒ|ÅŽ|Ç‘|Å|Æ |Ø|Ǿ/' => 'O', + '/ò|ó|ô|õ|Å|Å|Ç’|Å‘|Æ¡|ø|Ç¿|º/' => 'o', + '/Å”|Å–|Ř/' => 'R', + '/Å•|Å—|Å™/' => 'r', + '/Åš|Åœ|Åž|Å /' => 'S', + '/Å›|Å|ÅŸ|Å¡|Å¿/' => 's', + '/Å¢|Ť|Ŧ/' => 'T', + '/Å£|Å¥|ŧ/' => 't', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Å®|Ű|Ų|Ư|Ç“|Ç•|Ç—|Ç™|Ç›/' => 'U', + '/ù|ú|û|Å©|Å«|Å­|ů|ű|ų|ư|Ç”|Ç–|ǘ|Çš|Çœ/' => 'u', + '/Ã|Ÿ|Ŷ/' => 'Y', + '/ý|ÿ|Å·/' => 'y', + '/Å´/' => 'W', + '/ŵ/' => 'w', + '/Ź|Å»|Ž/' => 'Z', + '/ź|ż|ž/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/'=> 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Å’/' => 'OE', + '/Æ’/' => 'f' + ); + +/** + * Method cache array. + * + * @var array + */ + protected static $_cache = array(); + +/** + * The initial state of Inflector so reset() works. + * + * @var array + */ + protected static $_initialState = array(); + +/** + * Cache inflected values, and return if already available + * + * @param string $type Inflection type + * @param string $key Original value + * @param string $value Inflected value + * @return string Inflected value, from cache + */ + protected static function _cache($type, $key, $value = false) { + $key = '_' . $key; + $type = '_' . $type; + if ($value !== false) { + self::$_cache[$type][$key] = $value; + return $value; + } + if (!isset(self::$_cache[$type][$key])) { + return false; + } + return self::$_cache[$type][$key]; + } + +/** + * Clears Inflectors inflected value caches. And resets the inflection + * rules to the initial values. + * + * @return void + */ + public static function reset() { + if (empty(self::$_initialState)) { + self::$_initialState = get_class_vars('Inflector'); + return; + } + foreach (self::$_initialState as $key => $val) { + if ($key != '_initialState') { + self::${$key} = $val; + } + } + } + +/** + * Adds custom inflection $rules, of either 'plural', 'singular' or 'transliteration' $type. + * + * ### Usage: + * + * {{{ + * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); + * Inflector::rules('plural', array( + * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), + * 'uninflected' => array('dontinflectme'), + * 'irregular' => array('red' => 'redlings') + * )); + * Inflector::rules('transliteration', array('/Ã¥/' => 'aa')); + * }}} + * + * @param string $type The type of inflection, either 'plural', 'singular' or 'transliteration' + * @param array $rules Array of rules to be added. + * @param boolean $reset If true, will unset default inflections for all + * new rules that are being defined in $rules. + * @return void + */ + public static function rules($type, $rules, $reset = false) { + $var = '_' . $type; + + switch ($type) { + case 'transliteration': + if ($reset) { + self::$_transliteration = $rules; + } else { + self::$_transliteration = $rules + self::$_transliteration; + } + break; + + default: + foreach ($rules as $rule => $pattern) { + if (is_array($pattern)) { + if ($reset) { + self::${$var}[$rule] = $pattern; + } else { + self::${$var}[$rule] = array_merge($pattern, self::${$var}[$rule]); + } + unset($rules[$rule], self::${$var}['cache' . ucfirst($rule)]); + if (isset(self::${$var}['merged'][$rule])) { + unset(self::${$var}['merged'][$rule]); + } + if ($type === 'plural') { + self::$_cache['pluralize'] = self::$_cache['tableize'] = array(); + } elseif ($type === 'singular') { + self::$_cache['singularize'] = array(); + } + } + } + self::${$var}['rules'] = array_merge($rules, self::${$var}['rules']); + break; + } + } + +/** + * Return $word in plural form. + * + * @param string $word Word in singular + * @return string Word in plural + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function pluralize($word) { + + if (isset(self::$_cache['pluralize'][$word])) { + return self::$_cache['pluralize'][$word]; + } + + if (!isset(self::$_plural['merged']['irregular'])) { + self::$_plural['merged']['irregular'] = self::$_plural['irregular']; + } + + if (!isset(self::$_plural['merged']['uninflected'])) { + self::$_plural['merged']['uninflected'] = array_merge(self::$_plural['uninflected'], self::$_uninflected); + } + + if (!isset(self::$_plural['cacheUninflected']) || !isset(self::$_plural['cacheIrregular'])) { + self::$_plural['cacheUninflected'] = '(?:' . implode('|', self::$_plural['merged']['uninflected']) . ')'; + self::$_plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$_plural['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$_plural['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$_cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_plural['merged']['irregular'][strtolower($regs[2])], 1); + return self::$_cache['pluralize'][$word]; + } + + if (preg_match('/^(' . self::$_plural['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$_cache['pluralize'][$word] = $word; + return $word; + } + + foreach (self::$_plural['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); + return self::$_cache['pluralize'][$word]; + } + } + } + +/** + * Return $word in singular form. + * + * @param string $word Word in plural + * @return string Word in singular + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function singularize($word) { + + if (isset(self::$_cache['singularize'][$word])) { + return self::$_cache['singularize'][$word]; + } + + if (!isset(self::$_singular['merged']['uninflected'])) { + self::$_singular['merged']['uninflected'] = array_merge( + self::$_singular['uninflected'], + self::$_uninflected + ); + } + + if (!isset(self::$_singular['merged']['irregular'])) { + self::$_singular['merged']['irregular'] = array_merge( + self::$_singular['irregular'], + array_flip(self::$_plural['irregular']) + ); + } + + if (!isset(self::$_singular['cacheUninflected']) || !isset(self::$_singular['cacheIrregular'])) { + self::$_singular['cacheUninflected'] = '(?:' . join( '|', self::$_singular['merged']['uninflected']) . ')'; + self::$_singular['cacheIrregular'] = '(?:' . join( '|', array_keys(self::$_singular['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$_singular['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$_cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_singular['merged']['irregular'][strtolower($regs[2])], 1); + return self::$_cache['singularize'][$word]; + } + + if (preg_match('/^(' . self::$_singular['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$_cache['singularize'][$word] = $word; + return $word; + } + + foreach (self::$_singular['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word); + return self::$_cache['singularize'][$word]; + } + } + self::$_cache['singularize'][$word] = $word; + return $word; + } + +/** + * Returns the given lower_case_and_underscored_word as a CamelCased word. + * + * @param string $lowerCaseAndUnderscoredWord Word to camelize + * @return string Camelized word. LikeThis. + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function camelize($lowerCaseAndUnderscoredWord) { + if (!($result = self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { + $result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord)); + self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); + } + return $result; + } + +/** + * Returns the given camelCasedWord as an underscored_word. + * + * @param string $camelCasedWord Camel-cased word to be "underscorized" + * @return string Underscore-syntaxed version of the $camelCasedWord + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function underscore($camelCasedWord) { + if (!($result = self::_cache(__FUNCTION__, $camelCasedWord))) { + $result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord)); + self::_cache(__FUNCTION__, $camelCasedWord, $result); + } + return $result; + } + +/** + * Returns the given underscored_word_group as a Human Readable Word Group. + * (Underscores are replaced by spaces and capitalized following words.) + * + * @param string $lowerCaseAndUnderscoredWord String to be made more readable + * @return string Human-readable string + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function humanize($lowerCaseAndUnderscoredWord) { + if (!($result = self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { + $result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord)); + self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); + } + return $result; + } + +/** + * Returns corresponding table name for given model $className. ("people" for the model class "Person"). + * + * @param string $className Name of class to get database table name for + * @return string Name of the database table for given class + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function tableize($className) { + if (!($result = self::_cache(__FUNCTION__, $className))) { + $result = Inflector::pluralize(Inflector::underscore($className)); + self::_cache(__FUNCTION__, $className, $result); + } + return $result; + } + +/** + * Returns Cake model class name ("Person" for the database table "people".) for given database table. + * + * @param string $tableName Name of database table to get class name for + * @return string Class name + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function classify($tableName) { + if (!($result = self::_cache(__FUNCTION__, $tableName))) { + $result = Inflector::camelize(Inflector::singularize($tableName)); + self::_cache(__FUNCTION__, $tableName, $result); + } + return $result; + } + +/** + * Returns camelBacked version of an underscored string. + * + * @param string $string + * @return string in variable form + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function variable($string) { + if (!($result = self::_cache(__FUNCTION__, $string))) { + $string2 = Inflector::camelize(Inflector::underscore($string)); + $replace = strtolower(substr($string2, 0, 1)); + $result = preg_replace('/\\w/', $replace, $string2, 1); + self::_cache(__FUNCTION__, $string, $result); + } + return $result; + } + +/** + * Returns a string with all spaces converted to underscores (by default), accented + * characters converted to non-accented characters, and non word characters removed. + * + * @param string $string the string you want to slug + * @param string $replacement will replace keys in map + * @return string + * @link http://book.cakephp.org/view/1479/Class-methods + */ + public static function slug($string, $replacement = '_') { + $quotedReplacement = preg_quote($replacement, '/'); + + $merge = array( + '/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ', + '/\\s+/' => $replacement, + sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '', + ); + + $map = self::$_transliteration + $merge; + return preg_replace(array_keys($map), array_values($map), $string); + } +} + +// Store the initial state +Inflector::reset(); diff --git a/app/Cake/Utility/ObjectCollection.php b/app/Cake/Utility/ObjectCollection.php new file mode 100644 index 00000000..2b6c6851 --- /dev/null +++ b/app/Cake/Utility/ObjectCollection.php @@ -0,0 +1,257 @@ +_loaded`. Enabled objects are stored in `$this->_enabled`. In addition + * the all support an `enabled` option that controls the enabled/disabled state of the object + * when loaded. + * + * @package Cake.Utility + * @since CakePHP(tm) v 2.0 + */ +abstract class ObjectCollection { + +/** + * List of the currently-enabled objects + * + * @var array + */ + protected $_enabled = array(); + +/** + * A hash of loaded objects, indexed by name + * + * @var array + */ + protected $_loaded = array(); + +/** + * Loads a new object onto the collection. Can throw a variety of exceptions + * + * Implementations of this class support a `$options['callbacks']` flag which enables/disables + * a loaded object. + * + * @param string $name Name of object to load. + * @param array $options Array of configuration options for the object to be constructed. + * @return object the constructed object + */ + abstract public function load($name, $options = array()); + +/** + * Trigger a callback method on every object in the collection. + * Used to trigger methods on objects in the collection. Will fire the methods in the + * order they were attached. + * + * ### Options + * + * - `breakOn` Set to the value or values you want the callback propagation to stop on. + * Can either be a scalar value, or an array of values to break on. Defaults to `false`. + * + * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value + * will be returned. If used in combination with `collectReturn` the collected results will be returned. + * Defaults to `false`. + * + * - `collectReturn` Set to true to collect the return of each object into an array. + * This array of return values will be returned from the trigger() call. Defaults to `false`. + * + * - `triggerDisabled` Will trigger the callback on all objects in the collection even the non-enabled + * objects. Defaults to false. + * + * - `modParams` Allows each object the callback gets called on to modify the parameters to the next object. + * Setting modParams to an integer value will allow you to modify the parameter with that index. + * Any non-null value will modify the parameter index indicated. + * Defaults to false. + * + * + * @param string $callback Method to fire on all the objects. Its assumed all the objects implement + * the method you are calling. + * @param array $params Array of parameters for the triggered callback. + * @param array $options Array of options. + * @return mixed Either the last result or all results if collectReturn is on. + * @throws CakeException when modParams is used with an index that does not exist. + */ + public function trigger($callback, $params = array(), $options = array()) { + if (empty($this->_enabled)) { + return true; + } + $options = array_merge( + array( + 'break' => false, + 'breakOn' => false, + 'collectReturn' => false, + 'triggerDisabled' => false, + 'modParams' => false + ), + $options + ); + $collected = array(); + $list = $this->_enabled; + if ($options['triggerDisabled'] === true) { + $list = array_keys($this->_loaded); + } + if ($options['modParams'] !== false && !isset($params[$options['modParams']])) { + throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.')); + } + foreach ($list as $name) { + $result = call_user_func_array(array($this->_loaded[$name], $callback), $params); + if ($options['collectReturn'] === true) { + $collected[] = $result; + } + if ( + $options['break'] && ($result === $options['breakOn'] || + (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true))) + ) { + return $result; + } elseif ($options['modParams'] !== false && is_array($result)) { + $params[$options['modParams']] = $result; + } + } + if ($options['modParams'] !== false) { + return $params[$options['modParams']]; + } + return $options['collectReturn'] ? $collected : $result; + } + +/** + * Provide public read access to the loaded objects + * + * @param string $name Name of property to read + * @return mixed + */ + public function __get($name) { + if (isset($this->_loaded[$name])) { + return $this->_loaded[$name]; + } + return null; + } + +/** + * Provide isset access to _loaded + * + * @param string $name Name of object being checked. + * @return boolean + */ + public function __isset($name) { + return isset($this->_loaded[$name]); + } + +/** + * Enables callbacks on an object or array of objects + * + * @param mixed $name CamelCased name of the object(s) to enable (string or array) + * @return void + */ + public function enable($name) { + foreach ((array)$name as $object) { + if (isset($this->_loaded[$object]) && array_search($object, $this->_enabled) === false) { + $this->_enabled[] = $object; + } + } + } + +/** + * Disables callbacks on a object or array of objects. Public object methods are still + * callable as normal. + * + * @param mixed $name CamelCased name of the objects(s) to disable (string or array) + * @return void + */ + public function disable($name) { + foreach ((array)$name as $object) { + $index = array_search($object, $this->_enabled); + unset($this->_enabled[$index]); + } + $this->_enabled = array_values($this->_enabled); + } + +/** + * Gets the list of currently-enabled objects, or, the current status of a single objects + * + * @param string $name Optional. The name of the object to check the status of. If omitted, + * returns an array of currently-enabled object + * @return mixed If $name is specified, returns the boolean status of the corresponding object. + * Otherwise, returns an array of all enabled objects. + */ + public function enabled($name = null) { + if (!empty($name)) { + return in_array($name, $this->_enabled); + } + return $this->_enabled; + } + +/** + * Gets the list of attached behaviors, or, whether the given behavior is attached + * + * @param string $name Optional. The name of the behavior to check the status of. If omitted, + * returns an array of currently-attached behaviors + * @return mixed If $name is specified, returns the boolean status of the corresponding behavior. + * Otherwise, returns an array of all attached behaviors. + */ + public function attached($name = null) { + if (!empty($name)) { + return isset($this->_loaded[$name]); + } + return array_keys($this->_loaded); + } + +/** + * Name of the object to remove from the collection + * + * @param string $name Name of the object to delete. + * @return void + */ + public function unload($name) { + list($plugin, $name) = pluginSplit($name); + unset($this->_loaded[$name]); + $this->_enabled = array_values(array_diff($this->_enabled, (array)$name)); + } + +/** + * Adds or overwrites an instantiated object to the collection + * + * @param string $name Name of the object + * @param Object $object The object to use + * @return array Loaded objects + */ + public function set($name = null, $object = null) { + if (!empty($name) && !empty($object)) { + list($plugin, $name) = pluginSplit($name); + $this->_loaded[$name] = $object; + } + return $this->_loaded; + } + +/** + * Normalizes an object array, creates an array that makes lazy loading + * easier + * + * @param array $objects Array of child objects to normalize. + * @return array Array of normalized objects. + */ + public static function normalizeObjectArray($objects) { + $normal = array(); + foreach ($objects as $i => $objectName) { + $options = array(); + if (!is_int($i)) { + list($options, $objectName) = array($objectName, $i); + } + list($plugin, $name) = pluginSplit($objectName); + $normal[$name] = array('class' => $objectName, 'settings' => $options); + } + return $normal; + } +} \ No newline at end of file diff --git a/app/Cake/Utility/Sanitize.php b/app/Cake/Utility/Sanitize.php new file mode 100644 index 00000000..90689850 --- /dev/null +++ b/app/Cake/Utility/Sanitize.php @@ -0,0 +1,256 @@ + $clean) { + $cleaned[$key] = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $clean); + } + } else { + $cleaned = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $string); + } + return $cleaned; + } + +/** + * Makes a string SQL-safe. + * + * @param string $string String to sanitize + * @param string $connection Database connection being used + * @return string SQL safe string + */ + public static function escape($string, $connection = 'default') { + $db = ConnectionManager::getDataSource($connection); + if (is_numeric($string) || $string === null || is_bool($string)) { + return $string; + } + $string = substr($db->value($string), 1); + $string = substr($string, 0, -1); + return $string; + } + +/** + * Returns given string safe for display as HTML. Renders entities. + * + * strip_tags() does not validating HTML syntax or structure, so it might strip whole passages + * with broken HTML. + * + * ### Options: + * + * - remove (boolean) if true strips all HTML tags before encoding + * - charset (string) the charset used to encode the string + * - quotes (int) see http://php.net/manual/en/function.htmlentities.php + * - double (boolean) doube encode html entities + * + * @param string $string String from where to strip tags + * @param array $options Array of options to use. + * @return string Sanitized string + */ + public static function html($string, $options = array()) { + static $defaultCharset = false; + if ($defaultCharset === false) { + $defaultCharset = Configure::read('App.encoding'); + if ($defaultCharset === null) { + $defaultCharset = 'UTF-8'; + } + } + $default = array( + 'remove' => false, + 'charset' => $defaultCharset, + 'quotes' => ENT_QUOTES, + 'double' => true + ); + + $options = array_merge($default, $options); + + if ($options['remove']) { + $string = strip_tags($string); + } + + return htmlentities($string, $options['quotes'], $options['charset'], $options['double']); + } + +/** + * Strips extra whitespace from output + * + * @param string $str String to sanitize + * @return string whitespace sanitized string + */ + public static function stripWhitespace($str) { + $r = preg_replace('/[\n\r\t]+/', '', $str); + return preg_replace('/\s{2,}/u', ' ', $r); + } + +/** + * Strips image tags from output + * + * @param string $str String to sanitize + * @return string Sting with images stripped. + */ + public static function stripImages($str) { + $str = preg_replace('/(]*>)(]+alt=")([^"]*)("[^>]*>)(<\/a>)/i', '$1$3$5
', $str); + $str = preg_replace('/(]+alt=")([^"]*)("[^>]*>)/i', '$2
', $str); + $str = preg_replace('/]*>/i', '', $str); + return $str; + } + +/** + * Strips scripts and stylesheets from output + * + * @param string $str String to sanitize + * @return string String with ', + 'javascriptstart' => '', + 'javascriptend' => '' + ); + +/** + * Minimized attributes + * + * @var array + */ + protected $_minimizedAttributes = array( + 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected', + 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize' + ); + +/** + * Format to attribute + * + * @var string + */ + protected $_attributeFormat = '%s="%s"'; + +/** + * Format to attribute + * + * @var string + */ + protected $_minimizedAttributeFormat = '%s="%s"'; + +/** + * Breadcrumbs. + * + * @var array + */ + protected $_crumbs = array(); + +/** + * Names of script files that have been included once + * + * @var array + */ + protected $_includedScripts = array(); +/** + * Options for the currently opened script block buffer if any. + * + * @var array + */ + protected $_scriptBlockOptions = array(); +/** + * Document type definitions + * + * @var array + */ + protected $_docTypes = array( + 'html4-strict' => '', + 'html4-trans' => '', + 'html4-frame' => '', + 'html5' => '', + 'xhtml-strict' => '', + 'xhtml-trans' => '', + 'xhtml-frame' => '', + 'xhtml11' => '' + ); + +/** + * Constructor + * + * ### Settings + * + * - `configFile` A file containing an array of tags you wish to redefine. + * + * ### Customizing tag sets + * + * Using the `configFile` option you can redefine the tag HtmlHelper will use. + * The file named should be compatible with HtmlHelper::loadConfig(). + * + * @param View $View The View this helper is being attached to. + * @param array $settings Configuration settings for the helper. + */ + public function __construct(View $View, $settings = array()) { + parent::__construct($View, $settings); + if (!empty($settings['configFile'])) { + $this->loadConfig($settings['configFile']); + } + } + +/** + * Adds a link to the breadcrumbs array. + * + * @param string $name Text for link + * @param string $link URL for link (if empty it won't be a link) + * @param mixed $options Link attributes e.g. array('id'=>'selected') + * @return void + * @see HtmlHelper::link() for details on $options that can be used. + */ + public function addCrumb($name, $link = null, $options = null) { + $this->_crumbs[] = array($name, $link, $options); + } + +/** + * Returns a doctype string. + * + * Possible doctypes: + * + * - html4-strict: HTML4 Strict. + * - html4-trans: HTML4 Transitional. + * - html4-frame: HTML4 Frameset. + * - html5: HTML5. + * - xhtml-strict: XHTML1 Strict. + * - xhtml-trans: XHTML1 Transitional. + * - xhtml-frame: XHTML1 Frameset. + * - xhtml11: XHTML1.1. + * + * @param string $type Doctype to use. + * @return string Doctype string + * @link http://book.cakephp.org/view/1439/docType + */ + public function docType($type = 'xhtml-strict') { + if (isset($this->_docTypes[$type])) { + return $this->_docTypes[$type]; + } + return null; + } + +/** + * Creates a link to an external resource and handles basic meta tags + * + * ### Options + * + * - `inline` Whether or not the link element should be output inline, or in scripts_for_layout. + * + * @param string $type The title of the external resource + * @param mixed $url The address of the external resource or string for content attribute + * @param array $options Other attributes for the generated tag. If the type attribute is html, + * rss, atom, or icon, the mime-type is returned. + * @return string A completed `` element. + * @link http://book.cakephp.org/view/1438/meta + */ + public function meta($type, $url = null, $options = array()) { + $inline = isset($options['inline']) ? $options['inline'] : true; + unset($options['inline']); + + if (!is_array($type)) { + $types = array( + 'rss' => array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url), + 'atom' => array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url), + 'icon' => array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url), + 'keywords' => array('name' => 'keywords', 'content' => $url), + 'description' => array('name' => 'description', 'content' => $url), + ); + + if ($type === 'icon' && $url === null) { + $types['icon']['link'] = $this->webroot('favicon.ico'); + } + + if (isset($types[$type])) { + $type = $types[$type]; + } elseif (!isset($options['type']) && $url !== null) { + if (is_array($url) && isset($url['ext'])) { + $type = $types[$url['ext']]; + } else { + $type = $types['rss']; + } + } elseif (isset($options['type']) && isset($types[$options['type']])) { + $type = $types[$options['type']]; + unset($options['type']); + } else { + $type = array(); + } + } elseif ($url !== null) { + $inline = $url; + } + $options = array_merge($type, $options); + $out = null; + + if (isset($options['link'])) { + if (isset($options['rel']) && $options['rel'] === 'icon') { + $out = sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' ')); + $options['rel'] = 'shortcut icon'; + } else { + $options['link'] = $this->url($options['link'], true); + } + $out .= sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' ')); + } else { + $out = sprintf($this->_tags['meta'], $this->_parseAttributes($options, array('type'), ' ', ' ')); + } + + if ($inline) { + return $out; + } else { + $this->_View->addScript($out); + } + } + +/** + * Returns a charset META-tag. + * + * @param string $charset The character set to be used in the meta tag. If empty, + * The App.encoding value will be used. Example: "utf-8". + * @return string A meta tag containing the specified character set. + * @link http://book.cakephp.org/view/1436/charset + */ + public function charset($charset = null) { + if (empty($charset)) { + $charset = strtolower(Configure::read('App.encoding')); + } + return sprintf($this->_tags['charset'], (!empty($charset) ? $charset : 'utf-8')); + } + +/** + * Creates an HTML link. + * + * If $url starts with "http://" this is treated as an external link. Else, + * it is treated as a path to controller/action and parsed with the + * HtmlHelper::url() method. + * + * If the $url is empty, $title is used instead. + * + * ### Options + * + * - `escape` Set to false to disable escaping of title and attributes. + * + * @param string $title The content to be wrapped by tags. + * @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://) + * @param array $options Array of HTML attributes. + * @param string $confirmMessage JavaScript confirmation message. + * @return string An `` element. + * @link http://book.cakephp.org/view/1442/link + */ + public function link($title, $url = null, $options = array(), $confirmMessage = false) { + $escapeTitle = true; + if ($url !== null) { + $url = $this->url($url); + } else { + $url = $this->url($title); + $title = $url; + $escapeTitle = false; + } + + if (isset($options['escape'])) { + $escapeTitle = $options['escape']; + } + + if ($escapeTitle === true) { + $title = h($title); + } elseif (is_string($escapeTitle)) { + $title = htmlentities($title, ENT_QUOTES, $escapeTitle); + } + + if (!empty($options['confirm'])) { + $confirmMessage = $options['confirm']; + unset($options['confirm']); + } + if ($confirmMessage) { + $confirmMessage = str_replace("'", "\'", $confirmMessage); + $confirmMessage = str_replace('"', '\"', $confirmMessage); + $options['onclick'] = "return confirm('{$confirmMessage}');"; + } elseif (isset($options['default']) && $options['default'] == false) { + if (isset($options['onclick'])) { + $options['onclick'] .= ' event.returnValue = false; return false;'; + } else { + $options['onclick'] = 'event.returnValue = false; return false;'; + } + unset($options['default']); + } + return sprintf($this->_tags['link'], $url, $this->_parseAttributes($options), $title); + } + +/** + * Creates a link element for CSS stylesheets. + * + * ### Usage + * + * Include one CSS file: + * + * `echo $this->Html->css('styles.css');` + * + * Include multiple CSS files: + * + * `echo $this->Html->css(array('one.css', 'two.css'));` + * + * Add the stylesheet to the `$scripts_for_layout` layout var: + * + * `$this->Html->css('styles.css', null, array('inline' => false));` + * + * ### Options + * + * - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true + * + * @param mixed $path The name of a CSS style sheet or an array containing names of + * CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot + * of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css. + * @param string $rel Rel attribute. Defaults to "stylesheet". If equal to 'import' the stylesheet will be imported. + * @param array $options Array of HTML attributes. + * @return string CSS or + + +

+ + \ No newline at end of file diff --git a/app/Cake/View/Layouts/js/default.ctp b/app/Cake/View/Layouts/js/default.ctp new file mode 100644 index 00000000..d94dc903 --- /dev/null +++ b/app/Cake/View/Layouts/js/default.ctp @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/Cake/View/Layouts/rss/default.ctp b/app/Cake/View/Layouts/rss/default.ctp new file mode 100644 index 00000000..d57624e1 --- /dev/null +++ b/app/Cake/View/Layouts/rss/default.ctp @@ -0,0 +1,14 @@ +Rss->document( + $this->Rss->channel( + array(), $channel, $content_for_layout + ) +); +?> diff --git a/app/Cake/View/Layouts/xml/default.ctp b/app/Cake/View/Layouts/xml/default.ctp new file mode 100644 index 00000000..1025a55f --- /dev/null +++ b/app/Cake/View/Layouts/xml/default.ctp @@ -0,0 +1,2 @@ +'; ?> + diff --git a/app/Cake/View/MediaView.php b/app/Cake/View/MediaView.php new file mode 100644 index 00000000..eeb789f1 --- /dev/null +++ b/app/Cake/View/MediaView.php @@ -0,0 +1,248 @@ +viewClass = 'Media'; + * $params = array( + * 'id' => 'example.zip', + * 'name' => 'example', + * 'download' => true, + * 'extension' => 'zip', + * 'path' => APP . 'files' . DS + * ); + * $this->set($params); + * } + * } + * }}} + * + * @package Cake.View + */ +class MediaView extends View { +/** + * Indicates whether response gzip compression was enabled for this class + * + * @var boolean + */ + protected $_compressionEnabled = false; + +/** + * Reference to the Response object responsible for sending the headers + * + * @var CakeResponse + */ + public $response = null; + +/** + * Constructor + * + * @param Controller $controller The controller with viewVars + */ + public function __construct($controller = null) { + parent::__construct($controller); + if (is_object($controller) && isset($controller->response)) { + $this->response = $controller->response; + } else { + $this->response = new CakeResponse; + } + } + +/** + * Display or download the given file + * + * @param string $view Not used + * @param string $layout Not used + * @return mixed + * @throws NotFoundException + */ + public function render($view = null, $layout = null) { + $name = $download = $extension = $id = $modified = $path = $cache = $mimeType = $compress = null; + extract($this->viewVars, EXTR_OVERWRITE); + + if (is_dir($path)) { + $path = $path . $id; + } else { + $path = APP . $path . $id; + } + + if (!is_file($path)) { + if (Configure::read('debug')) { + throw new NotFoundException(sprintf('The requested file %s was not found', $path)); + } + throw new NotFoundException('The requested file was not found'); + } + + if (is_array($mimeType)) { + $this->response->type($mimeType); + } + + if (isset($extension) && $this->_isActive()) { + $extension = strtolower($extension); + $chunkSize = 8192; + $buffer = ''; + $fileSize = @filesize($path); + $handle = fopen($path, 'rb'); + + if ($handle === false) { + return false; + } + if (!empty($modified) && !is_numeric($modified)) { + $modified = strtotime($modified, time()); + } else { + $modified = time(); + } + if ($this->response->type($extension) === false) { + $download = true; + } + + if ($cache) { + $this->response->cache($modified, $cache); + } else { + $this->response->header(array( + 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', + 'Expires' => '0', + 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', + 'Pragma' => 'no-cache' + )); + } + + if ($download) { + $agent = env('HTTP_USER_AGENT'); + + if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) { + $contentType = 'application/octetstream'; + } else if (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { + $contentType = 'application/force-download'; + } + + if (!empty($contentType)) { + $this->response->type($contentType); + } + if (is_null($name)) { + $name = $id; + } + $this->response->download($name); + $this->response->header(array('Accept-Ranges' => 'bytes')); + + $httpRange = env('HTTP_RANGE'); + if (isset($httpRange)) { + list($toss, $range) = explode('=', $httpRange); + + $size = $fileSize - 1; + $length = $fileSize - $range; + + $this->response->header(array( + 'Content-Length' => $length, + 'Content-Range' => 'bytes ' . $range . $size . '/' . $fileSize + )); + + $this->response->statusCode(206); + fseek($handle, $range); + } else { + $this->response->header('Content-Length', $fileSize); + } + } else { + $this->response->header(array( + 'Content-Length' => $fileSize + )); + } + $this->_clearBuffer(); + if ($compress) { + $this->_compressionEnabled = $this->response->compress(); + } + + $this->response->send(); + return $this->_sendFile($handle); + } + + return false; + } + +/** + * Reads out a file handle, and echos the content to the client. + * + * @param resource $handle A file handle or stream + * @return void + */ + protected function _sendFile($handle) { + $chunkSize = 8192; + $buffer = ''; + while (!feof($handle)) { + if (!$this->_isActive()) { + fclose($handle); + return false; + } + set_time_limit(0); + $buffer = fread($handle, $chunkSize); + echo $buffer; + if (!$this->_compressionEnabled) { + $this->_flushBuffer(); + } + } + fclose($handle); + } + +/** + * Returns true if connection is still active + * + * @return boolean + */ + protected function _isActive() { + return connection_status() == 0 && !connection_aborted(); + } + +/** + * Clears the contents of the topmost output buffer and discards them + * + * @return boolean + */ + protected function _clearBuffer() { + return @ob_end_clean(); + } + +/** + * Flushes the contents of the output buffer + * + * @return void + */ + protected function _flushBuffer() { + @flush(); + @ob_flush(); + } +} diff --git a/app/Cake/View/Pages/home.ctp b/app/Cake/View/Pages/home.ctp new file mode 100644 index 00000000..2f448880 --- /dev/null +++ b/app/Cake/View/Pages/home.ctp @@ -0,0 +1,173 @@ + + +

+ + 0): + Debugger::checkSecurityKeys(); +endif; +?> +

+ + 1) Help me configure it + 2) I don't / can't use URL rewriting +

+

+ '; + echo __d('cake_dev', 'Your tmp directory is writable.'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your tmp directory is NOT writable.'); + echo ''; + endif; + ?> +

+

+ '; + echo __d('cake_dev', 'The %s is being used for caching. To change the config edit APP/Config/core.php ', ''. $settings['engine'] . 'Engine'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in APP/Config/core.php'); + echo ''; + endif; + ?> +

+

+ '; + echo __d('cake_dev', 'Your database configuration file is present.'); + $filePresent = true; + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your database configuration file is NOT present.'); + echo '
'; + echo __d('cake_dev', 'Rename APP/Config/database.php.default to APP/Config/database.php'); + echo '
'; + endif; + ?> +

+ +

+ isConnected()): + echo ''; + echo __d('cake_dev', 'Cake is able to connect to the database.'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Cake is NOT able to connect to the database.'); + echo ''; + endif; + ?> +

+ +'; + __d('cake_dev', 'PCRE has not been compiled with Unicode support.'); + echo '
'; + __d('cake_dev', 'Recompile PCRE with Unicode support by adding --enable-unicode-properties when configuring'); + echo '

'; + } +?> +

+

+ +To change its layout, create: APP/View/Layouts/default.ctp.
+You can also add some CSS styles for your pages at: APP/webroot/css.'); +?> +

+ +

+

+ Html->link( + sprintf('%s %s', __d('cake_dev', 'New'), __d('cake_dev', 'CakePHP 1.3 Docs')), + 'http://book.cakephp.org/view/875/x1-3-Collection', + array('target' => '_blank', 'escape' => false) + ); + ?> +

+

+ Html->link( + __d('cake_dev', 'The 15 min Blog Tutorial'), + 'http://book.cakephp.org/view/1528/Blog', + array('target' => '_blank', 'escape' => false) + ); + ?> +

+ +

+

+ +

+

+ +

+ + diff --git a/app/Cake/View/ScaffoldView.php b/app/Cake/View/ScaffoldView.php new file mode 100644 index 00000000..62c46f04 --- /dev/null +++ b/app/Cake/View/ScaffoldView.php @@ -0,0 +1,89 @@ +action; + } + $name = Inflector::underscore($name); + $prefixes = Configure::read('Routing.prefixes'); + + if (!empty($prefixes)) { + foreach ($prefixes as $prefix) { + if (strpos($name, $prefix . '_') !== false) { + $name = substr($name, strlen($prefix) + 1); + break; + } + } + } + + if ($name === 'add' || $name == 'edit') { + $name = 'form'; + } + + $scaffoldAction = 'scaffold.' . $name; + + if (!is_null($this->subDir)) { + $subDir = strtolower($this->subDir) . DS; + } else { + $subDir = null; + } + + $names[] = $this->viewPath . DS . $subDir . $scaffoldAction; + $names[] = 'Scaffolds' . DS . $subDir . $name; + + $paths = $this->_paths($this->plugin); + $exts = array($this->ext); + if ($this->ext !== '.ctp') { + array_push($exts, '.ctp'); + } + foreach ($exts as $ext) { + foreach ($paths as $path) { + foreach ($names as $name) { + if (file_exists($path . $name . $ext)) { + return $path . $name . $ext; + } + } + } + } + + if ($name === 'Scaffolds' . DS . $subDir . 'error') { + return CAKE . 'View' . DS . 'Errors' . DS . 'scaffold_error.ctp'; + } + + throw new MissingViewException($paths[0] . $name . $this->ext); + } +} diff --git a/app/Cake/View/Scaffolds/form.ctp b/app/Cake/View/Scaffolds/form.ctp new file mode 100644 index 00000000..4341ef8f --- /dev/null +++ b/app/Cake/View/Scaffolds/form.ctp @@ -0,0 +1,51 @@ + +
+Form->create(); + echo $this->Form->inputs($scaffoldFields, array('created', 'modified', 'updated')); + echo $this->Form->end(__d('cake', 'Submit')); +?> +
+
+

+
    +request->action != 'add'): ?> +
  • Form->postLink( + __d('cake', 'Delete'), + array('action' => 'delete', $this->Form->value($modelClass . '.' . $primaryKey)), + null, + __d('cake', 'Are you sure you want to delete # %s?', $this->Form->value($modelClass . '.' . $primaryKey))); + ?>
  • + +
  • Html->link(__d('cake', 'List') . ' ' . $pluralHumanName, array('action' => 'index'));?>
  • + $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "\t\t
  • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' =>'index')) . "
  • \n"; + echo "\t\t
  • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' =>'add')) . "
  • \n"; + $done[] = $_details['controller']; + } + } + } +?> +
+
\ No newline at end of file diff --git a/app/Cake/View/Scaffolds/index.ctp b/app/Cake/View/Scaffolds/index.ctp new file mode 100644 index 00000000..d2559c6a --- /dev/null +++ b/app/Cake/View/Scaffolds/index.ctp @@ -0,0 +1,94 @@ + +
+

+ + + + + + + +"; + foreach ($scaffoldFields as $_field) { + $isKey = false; + if (!empty($associations['belongsTo'])) { + foreach ($associations['belongsTo'] as $_alias => $_details) { + if ($_field === $_details['foreignKey']) { + $isKey = true; + echo ""; + break; + } + } + } + if ($isKey !== true) { + echo ""; + } + } + + echo ''; + echo ''; + +endforeach; + +?> +
Paginator->sort($_field);?>
" . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "" . h(${$singularVar}[$modelClass][$_field]) . "'; + echo $this->Html->link(__d('cake', 'View'), array('action' => 'view', ${$singularVar}[$modelClass][$primaryKey])); + echo $this->Html->link(__d('cake', 'Edit'), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])); + echo $this->Form->postLink( + __d('cake', 'Delete'), + array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), + null, + __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] + ); + echo '
+

Paginator->counter(array( + 'format' => __d('cake', 'Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%') + )); + ?>

+
+ Paginator->prev('< ' . __d('cake', 'previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__d('cake', 'next') .' >', array(), null, array('class' => 'next disabled')); + ?> +
+
+
+

+
    +
  • Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')); ?>
  • + $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "
  • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
  • "; + echo "
  • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
  • "; + $done[] = $_details['controller']; + } + } + } +?> +
+
diff --git a/app/Cake/View/Scaffolds/view.ctp b/app/Cake/View/Scaffolds/view.ctp new file mode 100644 index 00000000..d18e7066 --- /dev/null +++ b/app/Cake/View/Scaffolds/view.ctp @@ -0,0 +1,146 @@ + +
+

+
+ $_details) { + if ($_field === $_details['foreignKey']) { + $isKey = true; + echo "\t\t
" . Inflector::humanize($_alias) . "
\n"; + echo "\t\t
\n\t\t\t" . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t 
\n"; + break; + } + } + } + if ($isKey !== true) { + echo "\t\t
" . Inflector::humanize($_field) . "
\n"; + echo "\t\t
" . h(${$singularVar}[$modelClass][$_field]) . " 
\n"; + } +} +?> +
+
+
+

+
    +" .$this->Html->link(__d('cake', 'Edit %s', $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])). " \n"; + echo "\t\t
  • " .$this->Html->link(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?'). "
  • \n"; + echo "\t\t
  • " .$this->Html->link(__d('cake', 'List %s', $pluralHumanName), array('action' => 'index')). "
  • \n"; + echo "\t\t
  • " .$this->Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')). "
  • \n"; + + $done = array(); + foreach ($associations as $_type => $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "\t\t
  • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
  • \n"; + echo "\t\t
  • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
  • \n"; + $done[] = $_details['controller']; + } + } + } +?> +
+
+ $_details): ?> + + $_details): +$otherSingularVar = Inflector::variable($_alias); +?> + + diff --git a/app/Cake/View/ThemeView.php b/app/Cake/View/ThemeView.php new file mode 100644 index 00000000..bd4cb885 --- /dev/null +++ b/app/Cake/View/ThemeView.php @@ -0,0 +1,71 @@ +theme` and `$this->viewClass = 'Theme'` + * in your Controller to use the ThemeView. + * + * Example of theme path with `$this->theme = 'super_hot';` Would be `app/View/Themed/SuperHot/Posts` + * + * @package Cake.View + */ +class ThemeView extends View { +/** + * Constructor for ThemeView sets $this->theme. + * + * @param Controller $controller Controller object to be rendered. + */ + public function __construct($controller) { + parent::__construct($controller); + $this->theme = $controller->theme; + } + +/** + * Return all possible paths to find view files in order + * + * @param string $plugin The name of the plugin views are being found for. + * @param boolean $cached Set to true to force dir scan. + * @return array paths + * @todo Make theme path building respect $cached parameter. + */ + protected function _paths($plugin = null, $cached = true) { + $paths = parent::_paths($plugin, $cached); + $themePaths = array(); + + if (!empty($this->theme)) { + $count = count($paths); + for ($i = 0; $i < $count; $i++) { + if (strpos($paths[$i], DS . 'Plugins' . DS) === false + && strpos($paths[$i], DS . 'Cake' . DS . 'View') === false) { + if ($plugin) { + $themePaths[] = $paths[$i] . 'Themed'. DS . $this->theme . DS . 'Plugins' . DS . $plugin . DS; + } + $themePaths[] = $paths[$i] . 'Themed'. DS . $this->theme . DS; + } + } + $paths = array_merge($themePaths, $paths); + } + return $paths; + } +} diff --git a/app/Cake/View/View.php b/app/Cake/View/View.php new file mode 100644 index 00000000..891feccc --- /dev/null +++ b/app/Cake/View/View.php @@ -0,0 +1,764 @@ +set()` + * + * @package Cake.View + * @property CacheHelper $Cache + * @property FormHelper $Form + * @property HtmlHelper $Html + * @property JsHelper $Js + * @property NumberHelper $Number + * @property PaginatorHelper $Paginator + * @property RssHelper $Rss + * @property SessionHelper $Session + * @property TextHelper $Text + * @property TimeHelper $Time + */ +class View extends Object { + +/** + * Helpers collection + * + * @var HelperCollection + */ + public $Helpers; + +/** + * Name of the plugin. + * + * @link http://manual.cakephp.org/chapter/plugins + * @var string + */ + public $plugin = null; + +/** + * Name of the controller. + * + * @var string Name of controller + */ + public $name = null; + +/** + * Current passed params + * + * @var mixed + */ + public $passedArgs = array(); + +/** + * An array of names of built-in helpers to include. + * + * @var mixed A single name as a string or a list of names as an array. + */ + public $helpers = array('Html'); + +/** + * Path to View. + * + * @var string Path to View + */ + public $viewPath = null; + +/** + * Variables for the view + * + * @var array + */ + public $viewVars = array(); + +/** + * Name of view to use with this View. + * + * @var string + */ + public $view = null; + +/** + * Name of layout to use with this View. + * + * @var string + */ + public $layout = 'default'; + +/** + * Path to Layout. + * + * @var string Path to Layout + */ + public $layoutPath = null; + +/** + * Turns on or off Cake's conventional mode of applying layout files. On by default. + * Setting to off means that layouts will not be automatically applied to rendered views. + * + * @var boolean + */ + public $autoLayout = true; + +/** + * File extension. Defaults to Cake's template ".ctp". + * + * @var string + */ + public $ext = '.ctp'; + +/** + * Sub-directory for this view file. This is often used for extension based routing. + * for example with an `xml` extension, $subDir would be `xml/` + * + * @var string + */ + public $subDir = null; + +/** + * Theme name. If you are using themes, you should remember to use ThemeView as well. + * + * @var string + */ + public $theme = null; + +/** + * Used to define methods a controller that will be cached. + * + * @see Controller::$cacheAction + * @var mixed + */ + public $cacheAction = false; + +/** + * holds current errors for the model validation + * + * @var array + */ + public $validationErrors = array(); + +/** + * True when the view has been rendered. + * + * @var boolean + */ + public $hasRendered = false; + +/** + * List of generated DOM UUIDs + * + * @var array + */ + public $uuids = array(); + +/** + * Holds View output. + * + * @var string + */ + public $output = false; + +/** + * An instance of a CakeRequest object that contains information about the current request. + * This object contains all the information about a request and several methods for reading + * additional information about the request. + * + * @var CakeRequest + */ + public $request; + +/** + * The Cache configuration View will use to store cached elements. Changing this will change + * the default configuration elements are stored under. You can also choose a cache config + * per element. + * + * @var string + * @see View::element() + */ + public $elementCache = 'default'; + +/** + * List of variables to collect from the associated controller + * + * @var array + */ + protected $_passedVars = array( + 'viewVars', 'autoLayout', 'ext', 'helpers', 'view', 'layout', 'name', + 'layoutPath', 'viewPath', 'request', 'plugin', 'passedArgs', 'cacheAction' + ); + +/** + * Scripts (and/or other tags) for the layout + * + * @var array + */ + protected $_scripts = array(); + +/** + * Holds an array of paths. + * + * @var array + */ + protected $_paths = array(); + +/** + * boolean to indicate that helpers have been loaded. + * + * @var boolean + */ + protected $_helpersLoaded = false; + +/** + * Constructor + * + * @param Controller $controller A controller object to pull View::__passedArgs from. + */ + public function __construct($controller) { + if (is_object($controller)) { + $count = count($this->_passedVars); + for ($j = 0; $j < $count; $j++) { + $var = $this->_passedVars[$j]; + $this->{$var} = $controller->{$var}; + } + } + $this->Helpers = new HelperCollection($this); + parent::__construct(); + } + +/** + * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string. + * + * This realizes the concept of Elements, (or "partial layouts") and the $params array is used to send + * data to be used in the element. Elements can be cached improving performance by using the `cache` option. + * + * @param string $name Name of template file in the/app/View/Elements/ folder + * @param array $data Array of data to be made available to the rendered view (i.e. the Element) + * @param array $options Array of options. Possible keys are: + * - `cache` - Can either be `true`, to enable caching using the config in View::$elementCache. Or an array + * If an array, the following keys can be used: + * - `config` - Used to store the cached element in a custom cache configuration. + * - `key` - Used to define the key used in the Cache::write(). It will be prefixed with `element_` + * - `plugin` - Load an element from a specific plugin. + * - `callbacks` - Set to true to fire beforeRender and afterRender helper callbacks for this element. + * Defaults to false. + * @return string Rendered Element + */ + public function element($name, $data = array(), $options = array()) { + $file = $plugin = $key = null; + $callbacks = false; + + if (isset($options['plugin'])) { + $plugin = Inflector::camelize($options['plugin']); + } + if (isset($this->plugin) && !$plugin) { + $plugin = $this->plugin; + } + if (isset($options['callbacks'])) { + $callbacks = $options['callbacks']; + } + + if (isset($options['cache'])) { + $underscored = null; + if ($plugin) { + $underscored = Inflector::underscore($plugin); + } + $keys = array_merge(array($underscored, $name), array_keys($options), array_keys($data)); + $caching = array( + 'config' => $this->elementCache, + 'key' => implode('_', $keys) + ); + if (is_array($options['cache'])) { + $defaults = array( + 'config' => $this->elementCache, + 'key' => $caching['key'] + ); + $caching = array_merge($defaults, $options['cache']); + } + $key = 'element_' . $caching['key']; + $contents = Cache::read($key, $caching['config']); + if ($contents !== false) { + return $contents; + } + } + + $file = $this->_getElementFilename($name, $plugin); + + if ($file) { + if (!$this->_helpersLoaded) { + $this->loadHelpers(); + } + if ($callbacks) { + $this->Helpers->trigger('beforeRender', array($file)); + } + $element = $this->_render($file, array_merge($this->viewVars, $data)); + if ($callbacks) { + $this->Helpers->trigger('afterRender', array($file, $element)); + } + if (isset($options['cache'])) { + Cache::write($key, $element, $caching['config']); + } + return $element; + } + $file = 'Elements' . DS . $name . $this->ext; + + if (Configure::read('debug') > 0) { + return "Element Not Found: " . $file; + } + } + +/** + * Renders view for given view file and layout. + * + * Render triggers helper callbacks, which are fired before and after the view are rendered, + * as well as before and after the layout. The helper callbacks are called + * + * - `beforeRender` + * - `afterRender` + * - `beforeLayout` + * - `afterLayout` + * + * If View::$autoRender is false and no `$layout` is provided, the view will be returned bare. + * + * @param string $view Name of view file to use + * @param string $layout Layout to use. + * @return string Rendered Element + * @throws CakeException if there is an error in the view. + */ + public function render($view = null, $layout = null) { + if ($this->hasRendered) { + return true; + } + if (!$this->_helpersLoaded) { + $this->loadHelpers(); + } + $this->output = null; + + if ($view !== false && $viewFileName = $this->_getViewFileName($view)) { + $this->Helpers->trigger('beforeRender', array($viewFileName)); + $this->output = $this->_render($viewFileName); + $this->Helpers->trigger('afterRender', array($viewFileName)); + } + + if ($layout === null) { + $layout = $this->layout; + } + if ($this->output === false) { + throw new CakeException(__d('cake_dev', "Error in view %s, got no content.", $viewFileName)); + } + if ($layout && $this->autoLayout) { + $this->output = $this->renderLayout($this->output, $layout); + } + $this->hasRendered = true; + return $this->output; + } + +/** + * Renders a layout. Returns output from _render(). Returns false on error. + * Several variables are created for use in layout. + * + * - `title_for_layout` - A backwards compatible place holder, you should set this value if you want more control. + * - `content_for_layout` - contains rendered view file + * - `scripts_for_layout` - contains scripts added to header + * + * @param string $content_for_layout Content to render in a view, wrapped by the surrounding layout. + * @param string $layout Layout name + * @return mixed Rendered output, or false on error + * @throws CakeException if there is an error in the view. + */ + public function renderLayout($content_for_layout, $layout = null) { + $layoutFileName = $this->_getLayoutFileName($layout); + if (empty($layoutFileName)) { + return $this->output; + } + if (!$this->_helpersLoaded) { + $this->loadHelpers(); + } + $this->Helpers->trigger('beforeLayout', array($layoutFileName)); + + $this->viewVars = array_merge($this->viewVars, array( + 'content_for_layout' => $content_for_layout, + 'scripts_for_layout' => implode("\n\t", $this->_scripts), + )); + + if (!isset($this->viewVars['title_for_layout'])) { + $this->viewVars['title_for_layout'] = Inflector::humanize($this->viewPath); + } + + $this->output = $this->_render($layoutFileName); + + if ($this->output === false) { + throw new CakeException(__d('cake_dev', "Error in layout %s, got no content.", $layoutFileName)); + } + + $this->Helpers->trigger('afterLayout', array($layoutFileName)); + return $this->output; + } + +/** + * Render cached view. Works in concert with CacheHelper and Dispatcher to + * render cached view files. + * + * @param string $filename the cache file to include + * @param string $timeStart the page render start time + * @return boolean Success of rendering the cached file. + */ + public function renderCache($filename, $timeStart) { + ob_start(); + include ($filename); + + if (Configure::read('debug') > 0 && $this->layout != 'xml') { + echo ""; + } + $out = ob_get_clean(); + + if (preg_match('/^/', $out, $match)) { + if (time() >= $match['1']) { + @unlink($filename); + unset ($out); + return false; + } else { + if ($this->layout === 'xml') { + header('Content-type: text/xml'); + } + $commentLength = strlen(''); + echo substr($out, $commentLength); + return true; + } + } + } + +/** + * Returns a list of variables available in the current View context + * + * @return array Array of the set view variable names. + */ + public function getVars() { + return array_keys($this->viewVars); + } + +/** + * Returns the contents of the given View variable(s) + * + * @param string $var The view var you want the contents of. + * @return mixed The content of the named var if its set, otherwise null. + */ + public function getVar($var) { + if (!isset($this->viewVars[$var])) { + return null; + } else { + return $this->viewVars[$var]; + } + } + +/** + * Adds a script block or other element to be inserted in $scripts_for_layout in + * the `` of a document layout + * + * @param string $name Either the key name for the script, or the script content. Name can be used to + * update/replace a script element. + * @param string $content The content of the script being added, optional. + * @return void + */ + public function addScript($name, $content = null) { + if (empty($content)) { + if (!in_array($name, array_values($this->_scripts))) { + $this->_scripts[] = $name; + } + } else { + $this->_scripts[$name] = $content; + } + } + +/** + * Generates a unique, non-random DOM ID for an object, based on the object type and the target URL. + * + * @param string $object Type of object, i.e. 'form' or 'link' + * @param string $url The object's target URL + * @return string + */ + public function uuid($object, $url) { + $c = 1; + $url = Router::url($url); + $hash = $object . substr(md5($object . $url), 0, 10); + while (in_array($hash, $this->uuids)) { + $hash = $object . substr(md5($object . $url . $c), 0, 10); + $c++; + } + $this->uuids[] = $hash; + return $hash; + } + +/** + * Allows a template or element to set a variable that will be available in + * a layout or other element. Analagous to Controller::set(). + * + * @param mixed $one A string or an array of data. + * @param mixed $two Value in case $one is a string (which then works as the key). + * Unused if $one is an associative array, otherwise serves as the values to $one's keys. + * @return void + */ + public function set($one, $two = null) { + $data = null; + if (is_array($one)) { + if (is_array($two)) { + $data = array_combine($one, $two); + } else { + $data = $one; + } + } else { + $data = array($one => $two); + } + if ($data == null) { + return false; + } + $this->viewVars = $data + $this->viewVars; + } + +/** + * Magic accessor for helpers. Provides access to attributes that were deprecated. + * + * @param string $name Name of the attribute to get. + * @return mixed + */ + public function __get($name) { + if (isset($this->Helpers->{$name})) { + return $this->Helpers->{$name}; + } + switch ($name) { + case 'base': + case 'here': + case 'webroot': + case 'data': + return $this->request->{$name}; + case 'action': + return isset($this->request->params['action']) ? $this->request->params['action'] : ''; + case 'params': + return $this->request; + } + return null; + } + +/** + * Interact with the HelperCollection to load all the helpers. + * + * @return void + */ + public function loadHelpers() { + $helpers = HelperCollection::normalizeObjectArray($this->helpers); + foreach ($helpers as $name => $properties) { + list($plugin, $class) = pluginSplit($properties['class']); + $this->{$class} = $this->Helpers->load($properties['class'], $properties['settings']); + } + $this->_helpersLoaded = true; + } + +/** + * Renders and returns output for given view filename with its + * array of data. + * + * @param string $___viewFn Filename of the view + * @param array $___dataForView Data to include in rendered view. If empty the current View::$viewVars will be used. + * @return string Rendered output + */ + protected function _render($___viewFn, $___dataForView = array()) { + if (empty($___dataForView)) { + $___dataForView = $this->viewVars; + } + + extract($___dataForView, EXTR_SKIP); + ob_start(); + + include $___viewFn; + + return ob_get_clean(); + } + +/** + * Loads a helper. Delegates to the `HelperCollection::load()` to load the helper + * + * @param string $helperName Name of the helper to load. + * @param array $settings Settings for the helper + * @return Helper a constructed helper object. + * @see HelperCollection::load() + */ + public function loadHelper($helperName, $settings = array()) { + return $this->Helpers->load($helperName, $settings); + } + +/** + * Returns filename of given action's template file (.ctp) as a string. + * CamelCased action names will be under_scored! This means that you can have + * LongActionNames that refer to long_action_names.ctp views. + * + * @param string $name Controller action to find template filename for + * @return string Template filename + * @throws MissingViewException when a view file could not be found. + */ + protected function _getViewFileName($name = null) { + $subDir = null; + + if (!is_null($this->subDir)) { + $subDir = $this->subDir . DS; + } + + if ($name === null) { + $name = $this->view; + } + $name = str_replace('/', DS, $name); + + if (strpos($name, DS) === false && $name[0] !== '.') { + $name = $this->viewPath . DS . $subDir . Inflector::underscore($name); + } elseif (strpos($name, DS) !== false) { + if ($name[0] === DS || $name[1] === ':') { + if (is_file($name)) { + return $name; + } + $name = trim($name, DS); + } else if ($name[0] === '.') { + $name = substr($name, 3); + } else { + $name = $this->viewPath . DS . $subDir . $name; + } + } + $paths = $this->_paths($this->plugin); + + $exts = $this->_getExtensions(); + foreach ($exts as $ext) { + foreach ($paths as $path) { + if (file_exists($path . $name . $ext)) { + return $path . $name . $ext; + } + } + } + $defaultPath = $paths[0]; + + if ($this->plugin) { + $pluginPaths = App::path('plugins'); + foreach ($paths as $path) { + if (strpos($path, $pluginPaths[0]) === 0) { + $defaultPath = $path; + break; + } + } + } + throw new MissingViewException(array('file' => $defaultPath . $name . $this->ext)); + } + +/** + * Returns layout filename for this template as a string. + * + * @param string $name The name of the layout to find. + * @return string Filename for layout file (.ctp). + * @throws MissingLayoutException when a layout cannot be located + */ + protected function _getLayoutFileName($name = null) { + if ($name === null) { + $name = $this->layout; + } + $subDir = null; + + if (!is_null($this->layoutPath)) { + $subDir = $this->layoutPath . DS; + } + $paths = $this->_paths($this->plugin); + $file = 'Layouts' . DS . $subDir . $name; + + $exts = $this->_getExtensions(); + foreach ($exts as $ext) { + foreach ($paths as $path) { + if (file_exists($path . $file . $ext)) { + return $path . $file . $ext; + } + } + } + throw new MissingLayoutException(array('file' => $paths[0] . $file . $this->ext)); + } + + +/** + * Get the extensions that view files can use. + * + * @return array Array of extensions view files use. + */ + protected function _getExtensions() { + $exts = array($this->ext); + if ($this->ext !== '.ctp') { + array_push($exts, '.ctp'); + } + return $exts; + } + +/** + * Finds an element filename, returns false on failure. + * + * @param string $name The name of the element to find. + * @param string $plugin The plugin name the element is in. + * @return mixed Either a string to the element filename or false when one can't be found. + */ + protected function _getElementFileName($name, $plugin = null) { + $paths = $this->_paths($plugin); + $exts = $this->_getExtensions(); + foreach ($exts as $ext) { + foreach ($paths as $path) { + if (file_exists($path . 'Elements' . DS . $name . $ext)) { + return $path . 'Elements' . DS . $name . $ext; + } + } + } + return false; + } + +/** + * Return all possible paths to find view files in order + * + * @param string $plugin Optional plugin name to scan for view files. + * @param boolean $cached Set to true to force a refresh of view paths. + * @return array paths + */ + protected function _paths($plugin = null, $cached = true) { + if ($plugin === null && $cached === true && !empty($this->_paths)) { + return $this->_paths; + } + $paths = array(); + $viewPaths = App::path('View'); + $corePaths = array_flip(App::core('View')); + if (!empty($plugin)) { + $count = count($viewPaths); + for ($i = 0; $i < $count; $i++) { + if (!isset($corePaths[$viewPaths[$i]])) { + $paths[] = $viewPaths[$i] . 'Plugins' . DS . $plugin . DS; + } + } + $paths = array_merge($paths, App::path('View', $plugin)); + } + + $this->_paths = array_unique(array_merge($paths, $viewPaths, array_keys($corePaths))); + return $this->_paths; + } +} diff --git a/app/Cake/basics.php b/app/Cake/basics.php new file mode 100644 index 00000000..ac1e6c07 --- /dev/null +++ b/app/Cake/basics.php @@ -0,0 +1,732 @@ + 0) { + $file = ''; + $line = ''; + if ($showFrom) { + $calledFrom = debug_backtrace(); + $file = substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1); + $line = $calledFrom[0]['line']; + } + $html = << +%s (line %s) +
+%s
+
+ +HTML; + $text = << $val) { + $sa[$key] = $val[$sortby]; + } + + if ($order == 'asc') { + asort($sa, $type); + } else { + arsort($sa, $type); + } + + foreach ($sa as $key => $val) { + $out[] = $array[$key]; + } + return $out; + } +} + +/** + * Convenience method for htmlspecialchars. + * + * @param string $text Text to wrap through htmlspecialchars + * @param boolean $double Encode existing html entities + * @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8' + * @return string Wrapped text + * @link http://book.cakephp.org/view/1132/h + */ +function h($text, $double = true, $charset = null) { + if (is_array($text)) { + $texts = array(); + foreach ($text as $k => $t) { + $texts[$k] = h($t, $double, $charset); + } + return $texts; + } + + static $defaultCharset = false; + if ($defaultCharset === false) { + $defaultCharset = Configure::read('App.encoding'); + if ($defaultCharset === null) { + $defaultCharset = 'UTF-8'; + } + } + if (is_string($double)) { + $charset = $double; + } + return htmlspecialchars($text, ENT_QUOTES, ($charset) ? $charset : $defaultCharset, $double); +} + +/** + * Splits a dot syntax plugin name into its plugin and classname. + * If $name does not have a dot, then index 0 will be null. + * + * Commonly used like `list($plugin, $name) = pluginSplit($name);` + * + * @param string $name The name you want to plugin split. + * @param boolean $dotAppend Set to true if you want the plugin to have a '.' appended to it. + * @param string $plugin Optional default plugin to use if no plugin is found. Defaults to null. + * @return array Array with 2 indexes. 0 => plugin name, 1 => classname + */ +function pluginSplit($name, $dotAppend = false, $plugin = null) { + if (strpos($name, '.') !== false) { + $parts = explode('.', $name, 2); + if ($dotAppend) { + $parts[0] .= '.'; + } + return $parts; + } + return array($plugin, $name); +} + +/** + * Print_r convenience function, which prints out
 tags around
+ * the output of given array. Similar to debug().
+ *
+ * @see	debug()
+ * @param array $var Variable to print out
+ * @link http://book.cakephp.org/view/1136/pr
+ */
+function pr($var) {
+	if (Configure::read('debug') > 0) {
+		echo '
';
+		print_r($var);
+		echo '
'; + } +} + +/** + * Merge a group of arrays + * + * @param array First array + * @param array Second array + * @param array Third array + * @param array Etc... + * @return array All array parameters merged into one + * @link http://book.cakephp.org/view/1124/am + */ +function am() { + $r = array(); + $args = func_get_args(); + foreach ($args as $a) { + if (!is_array($a)) { + $a = array($a); + } + $r = array_merge($r, $a); + } + return $r; +} + +/** + * Gets an environment variable from available sources, and provides emulation + * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on + * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom + * environment information. + * + * @param string $key Environment variable name. + * @return string Environment variable setting. + * @link http://book.cakephp.org/view/1130/env + */ +function env($key) { + if ($key === 'HTTPS') { + if (isset($_SERVER['HTTPS'])) { + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); + } + return (strpos(env('SCRIPT_URI'), 'https://') === 0); + } + + if ($key === 'SCRIPT_NAME') { + if (env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) { + $key = 'SCRIPT_URL'; + } + } + + $val = null; + if (isset($_SERVER[$key])) { + $val = $_SERVER[$key]; + } elseif (isset($_ENV[$key])) { + $val = $_ENV[$key]; + } elseif (getenv($key) !== false) { + $val = getenv($key); + } + + if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) { + $addr = env('HTTP_PC_REMOTE_ADDR'); + if ($addr !== null) { + $val = $addr; + } + } + + if ($val !== null) { + return $val; + } + + switch ($key) { + case 'SCRIPT_FILENAME': + if (defined('SERVER_IIS') && SERVER_IIS === true) { + return str_replace('\\\\', '\\', env('PATH_TRANSLATED')); + } + break; + case 'DOCUMENT_ROOT': + $name = env('SCRIPT_NAME'); + $filename = env('SCRIPT_FILENAME'); + $offset = 0; + if (!strpos($name, '.php')) { + $offset = 4; + } + return substr($filename, 0, strlen($filename) - (strlen($name) + $offset)); + break; + case 'PHP_SELF': + return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME')); + break; + case 'CGI_MODE': + return (PHP_SAPI === 'cgi'); + break; + case 'HTTP_BASE': + $host = env('HTTP_HOST'); + $parts = explode('.', $host); + $count = count($parts); + + if ($count === 1) { + return '.' . $host; + } elseif ($count === 2) { + return '.' . $host; + } elseif ($count === 3) { + $gTLD = array( + 'aero', + 'asia', + 'biz', + 'cat', + 'com', + 'coop', + 'edu', + 'gov', + 'info', + 'int', + 'jobs', + 'mil', + 'mobi', + 'museum', + 'name', + 'net', + 'org', + 'pro', + 'tel', + 'travel', + 'xxx' + ); + if (in_array($parts[1], $gTLD)) { + return '.' . $host; + } + } + array_shift($parts); + return '.' . implode('.', $parts); + break; + } + return null; +} + +/** + * Reads/writes temporary data to cache files or session. + * + * @param string $path File path within /tmp to save the file. + * @param mixed $data The data to save to the temporary file. + * @param mixed $expires A valid strtotime string when the data expires. + * @param string $target The target of the cached data; either 'cache' or 'public'. + * @return mixed The contents of the temporary file. + * @deprecated Please use Cache::write() instead + */ +function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { + if (Configure::read('Cache.disable')) { + return null; + } + $now = time(); + + if (!is_numeric($expires)) { + $expires = strtotime($expires, $now); + } + + switch (strtolower($target)) { + case 'cache': + $filename = CACHE . $path; + break; + case 'public': + $filename = WWW_ROOT . $path; + break; + case 'tmp': + $filename = TMP . $path; + break; + } + $timediff = $expires - $now; + $filetime = false; + + if (file_exists($filename)) { + $filetime = @filemtime($filename); + } + + if ($data === null) { + if (file_exists($filename) && $filetime !== false) { + if ($filetime + $timediff < $now) { + @unlink($filename); + } else { + $data = @file_get_contents($filename); + } + } + } elseif (is_writable(dirname($filename))) { + @file_put_contents($filename, $data); + } + return $data; +} + +/** + * Used to delete files in the cache directories, or clear contents of cache directories + * + * @param mixed $params As String name to be searched for deletion, if name is a directory all files in + * directory will be deleted. If array, names to be searched for deletion. If clearCache() without params, + * all files in app/tmp/cache/views will be deleted + * @param string $type Directory in tmp/cache defaults to view directory + * @param string $ext The file extension you are deleting + * @return true if files found and deleted false otherwise + */ +function clearCache($params = null, $type = 'views', $ext = '.php') { + if (is_string($params) || $params === null) { + $params = preg_replace('/\/\//', '/', $params); + $cache = CACHE . $type . DS . $params; + + if (is_file($cache . $ext)) { + @unlink($cache . $ext); + return true; + } elseif (is_dir($cache)) { + $files = glob($cache . '*'); + + if ($files === false) { + return false; + } + + foreach ($files as $file) { + if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { + @unlink($file); + } + } + return true; + } else { + $cache = array( + CACHE . $type . DS . '*' . $params . $ext, + CACHE . $type . DS . '*' . $params . '_*' . $ext + ); + $files = array(); + while ($search = array_shift($cache)) { + $results = glob($search); + if ($results !== false) { + $files = array_merge($files, $results); + } + } + if (empty($files)) { + return false; + } + foreach ($files as $file) { + if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { + @unlink($file); + } + } + return true; + } + } elseif (is_array($params)) { + foreach ($params as $file) { + clearCache($file, $type, $ext); + } + return true; + } + return false; +} + +/** + * Recursively strips slashes from all values in an array + * + * @param array $values Array of values to strip slashes + * @return mixed What is returned from calling stripslashes + * @link http://book.cakephp.org/view/1138/stripslashes_deep + */ +function stripslashes_deep($values) { + if (is_array($values)) { + foreach ($values as $key => $value) { + $values[$key] = stripslashes_deep($value); + } + } else { + $values = stripslashes($values); + } + return $values; +} + +/** + * Returns a translated string if one is found; Otherwise, the submitted message. + * + * @param string $singular Text to translate + * @param mixed $args Array with arguments or multiple arguments in function + * @return mixed translated string + * @link http://book.cakephp.org/view/1121/__ + */ +function __($singular, $args = null) { + if (!$singular) { + return; + } + + App::uses('I18n', 'I18n'); + $translated = I18n::translate($singular); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 1); + } + return vsprintf($translated, $args); +} + +/** + * Returns correct plural form of message identified by $singular and $plural for count $count. + * Some languages have more than one form for plural messages dependent on the count. + * + * @param string $singular Singular text to translate + * @param string $plural Plural text + * @param integer $count Count + * @param mixed $args Array with arguments or multiple arguments in function + * @return mixed plural form of translated string + */ +function __n($singular, $plural, $count, $args = null) { + if (!$singular) { + return; + } + + App::uses('I18n', 'I18n'); + $translated = I18n::translate($singular, $plural, null, 6, $count); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 3); + } + return vsprintf($translated, $args); +} + +/** + * Allows you to override the current domain for a single message lookup. + * + * @param string $domain Domain + * @param string $msg String to translate + * @param mixed $args Array with arguments or multiple arguments in function + * @return translated string + */ +function __d($domain, $msg, $args = null) { + if (!$msg) { + return; + } + App::uses('I18n', 'I18n'); + $translated = I18n::translate($msg, null, $domain); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 2); + } + return vsprintf($translated, $args); +} + +/** + * Allows you to override the current domain for a single plural message lookup. + * Returns correct plural form of message identified by $singular and $plural for count $count + * from domain $domain. + * + * @param string $domain Domain + * @param string $singular Singular string to translate + * @param string $plural Plural + * @param integer $count Count + * @param mixed $args Array with arguments or multiple arguments in function + * @return plural form of translated string + */ +function __dn($domain, $singular, $plural, $count, $args = null) { + if (!$singular) { + return; + } + App::uses('I18n', 'I18n'); + $translated = I18n::translate($singular, $plural, $domain, 6, $count); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 4); + } + return vsprintf($translated, $args); +} + +/** + * Allows you to override the current domain for a single message lookup. + * It also allows you to specify a category. + * + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $domain Domain + * @param string $msg Message to translate + * @param integer $category Category + * @param mixed $args Array with arguments or multiple arguments in function + * @return translated string + */ +function __dc($domain, $msg, $category, $args = null) { + if (!$msg) { + return; + } + App::uses('I18n', 'I18n'); + $translated = I18n::translate($msg, null, $domain, $category); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 3); + } + return vsprintf($translated, $args); +} + +/** + * Allows you to override the current domain for a single plural message lookup. + * It also allows you to specify a category. + * Returns correct plural form of message identified by $singular and $plural for count $count + * from domain $domain. + * + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $domain Domain + * @param string $singular Singular string to translate + * @param string $plural Plural + * @param integer $count Count + * @param integer $category Category + * @param mixed $args Array with arguments or multiple arguments in function + * @return plural form of translated string + */ +function __dcn($domain, $singular, $plural, $count, $category, $args = null) { + if (!$singular) { + return; + } + App::uses('I18n', 'I18n'); + $translated = I18n::translate($singular, $plural, $domain, $category, $count); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 5); + } + return vsprintf($translated, $args); +} + +/** + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $msg String to translate + * @param integer $category Category + * @param mixed $args Array with arguments or multiple arguments in function + * @return translated string + */ +function __c($msg, $category, $args = null) { + if (!$msg) { + return; + } + App::uses('I18n', 'I18n'); + $translated = I18n::translate($msg, null, null, $category); + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 2); + } + return vsprintf($translated, $args); +} + +/** + * Shortcut to Log::write. + * + * @param string $message Message to write to log + */ +function LogError($message) { + App::uses('CakeLog', 'Log'); + $bad = array("\n", "\r", "\t"); + $good = ' '; + CakeLog::write('error', str_replace($bad, $good, $message)); +} + +/** + * Searches include path for files. + * + * @param string $file File to look for + * @return Full path to file if exists, otherwise false + * @link http://book.cakephp.org/view/1131/fileExistsInPath + */ +function fileExistsInPath($file) { + $paths = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($paths as $path) { + $fullPath = $path . DS . $file; + + if (file_exists($fullPath)) { + return $fullPath; + } elseif (file_exists($file)) { + return $file; + } + } + return false; +} + +/** + * Convert forward slashes to underscores and removes first and last underscores in a string + * + * @param string String to convert + * @return string with underscore remove from start and end of string + * @link http://book.cakephp.org/view/1126/convertSlash + */ +function convertSlash($string) { + $string = trim($string, '/'); + $string = preg_replace('/\/\//', '/', $string); + $string = str_replace('/', '_', $string); + return $string; +} diff --git a/app/Cake/bootstrap.php b/app/Cake/bootstrap.php new file mode 100644 index 00000000..76be5ec5 --- /dev/null +++ b/app/Cake/bootstrap.php @@ -0,0 +1,154 @@ +Duis tellus nunc, egestas a interdum sed, congue vitae magna. Curabitur a tellus quis lacus blandit sagittis a sit amet elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas metus sed neque ultricies.\r\n[button size=small color=silver]Read more[/button]

\r\n','Services-LEFT',NULL),(12,'

Integer egestas ultricies urna vitae molestie. Donec nec facilisis nisi. Vivamus tempor feugiat velit gravida vehicula. Donec faucibus pellentesque ipsum id varius. Ut rutrum metus sed neque ultricies a dictum ante sagittis.\r\n[button size=small color=silver]Read more[/button]

\r\n','Services-CENTER',NULL),(13,'

Praesent et metus sit amet nisl luctus commodo ut a risus. Mauris vehicula, ligula quis consectetur feugiat, arcu nibh tempor nisi, at varius dolor dolor nec dolor. Donec auctor mi vitae neque. Praesent sollicitudin egestas felis vitae gravida.\r\n[button size=small color=silver]Read more[/button]\r\n

\r\n','Services-RIGHT',NULL); +/*!40000 ALTER TABLE `qa_block_custom` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_block_regions` +-- + +DROP TABLE IF EXISTS `qa_block_regions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_block_regions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `block_id` int(11) NOT NULL, + `theme` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `region` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `ordering` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`,`block_id`) +) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_block_regions` +-- + +LOCK TABLES `qa_block_regions` WRITE; +/*!40000 ALTER TABLE `qa_block_regions` DISABLE KEYS */; +INSERT INTO `qa_block_regions` VALUES (8,10,'AdminDefault','dashboard_main',1),(9,10,'Default','dashboard_main',1),(13,4,'AdminDefault','management-menu',1),(14,4,'Default','management-menu',1),(18,3,'AdminDefault','footer',1),(48,3,'Default','footer',1),(131,6,'Default','main-menu',1),(133,26,'Default','slider',1),(140,9,'Default','language-switcher',1),(151,19,'Default','services-left',1),(153,20,'Default','services-center',1),(155,25,'Default','services-right',1),(157,15,'Default','search',1),(159,12,'Default','services-left',2),(161,11,'Default','services-center',2),(163,13,'Default','services-right',2),(165,14,'Default','slider',2),(166,15,'AdminDefault','dashboard_sidebar',1),(169,7,'AdminDefault','dashboard_sidebar',2),(172,5,'Default','user-menu',2); +/*!40000 ALTER TABLE `qa_block_regions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_block_roles` +-- + +DROP TABLE IF EXISTS `qa_block_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_block_roles` ( + `block_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `user_role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from users_roles.rid.', + PRIMARY KEY (`block_id`,`user_role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_block_roles` +-- + +LOCK TABLES `qa_block_roles` WRITE; +/*!40000 ALTER TABLE `qa_block_roles` DISABLE KEYS */; +INSERT INTO `qa_block_roles` VALUES ('1',3),('5',2); +/*!40000 ALTER TABLE `qa_block_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_blocks` +-- + +DROP TABLE IF EXISTS `qa_blocks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_blocks` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key - Unique block ID.', + `module` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The module from which the block originates; for example, ’user’ for the Who’s Online block, and ’block’ for any custom blocks.', + `delta` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'Unique ID for block within a module. Or menu_id', + `clone_of` int(11) NOT NULL DEFAULT '0', + `themes_cache` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'store all themes that belongs to (see block_regions table)', + `ordering` int(11) NOT NULL DEFAULT '1', + `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT 'Block enabled status. (1 = enabled, 0 = disabled)', + `visibility` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Flag to indicate how to show blocks on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)', + `pages` text COLLATE utf8_unicode_ci COMMENT 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.', + `title` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)', + `locale` text COLLATE utf8_unicode_ci, + `settings` text COLLATE utf8_unicode_ci, + `params` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores block settings, such as region and visibility...'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_blocks` +-- + +LOCK TABLES `qa_blocks` WRITE; +/*!40000 ALTER TABLE `qa_blocks` DISABLE KEYS */; +INSERT INTO `qa_blocks` VALUES (1,'user','login',0,'',1,0,0,'','User Login','a:0:{}','',NULL),(2,'menu','navigation',0,':Default:',2,1,0,'','',NULL,'',NULL),(3,'system','powered_by',0,':AdminDefault:Default:',1,1,0,'','Powered By','a:0:{}','',NULL),(4,'menu','management',0,':AdminDefault:',1,1,1,'/admin/*','','a:0:{}','',NULL),(5,'menu','user-menu',0,':Default:',4,1,0,'','User Menu','a:0:{}','',NULL),(6,'menu','main-menu',0,':Default:',1,1,0,'','','a:0:{}','',NULL),(7,'user','new',0,':AdminDefault:',5,1,0,'','New Users','a:0:{}','a:1:{s:10:\"show_limit\";s:1:\"5\";}',NULL),(9,'locale','language_switcher',0,':Default:',3,1,0,'','Language switcher','a:0:{}','a:2:{s:5:\"flags\";s:1:\"1\";s:4:\"name\";s:1:\"1\";}',NULL),(10,'system','recent_content',0,':AdminDefault:',1,1,0,'','Updates','a:0:{}','',NULL),(11,'block','5',0,':Default:',1,1,0,'','WHAT WE DO','a:0:{}','',NULL),(12,'block','6',0,':Default:',1,1,0,'','OUR MISSION','a:0:{}','',NULL),(13,'block','7',0,':Default:',1,1,0,'','WHO WE ARE','a:0:{}','',NULL),(14,'theme_default','slider',0,':Default:',1,1,1,'/','Slider','a:0:{}','a:1:{s:12:\"slider_order\";s:52:\"1_[language].jpg\r\n2_[language].jpg\r\n3_[language].jpg\";}',NULL),(15,'node','search',0,':AdminDefault:Default:',1,1,0,'','Search','a:0:{}','',NULL),(16,'taxonomy','vocabularies',0,NULL,1,1,0,NULL,'Vocabularies',NULL,NULL,NULL); +/*!40000 ALTER TABLE `qa_blocks` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_comments` +-- + +DROP TABLE IF EXISTS `qa_comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_comments` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key: Unique comment ID.', + `node_id` int(11) NOT NULL COMMENT 'The node.nid to which this comment is a reply.', + `user_id` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid who authored the comment. If set to 0, this comment was created by an anonymous user.', + `body` text COLLATE utf8_unicode_ci, + `subject` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `hostname` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The author’s host name. (IP)', + `homepage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was created, as a Unix timestamp.', + `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was last edited, as a Unix timestamp.', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT 'The published status of a comment. (0 = Not Published, 1 = Published)', + `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s name. Uses users.name if the user is logged in, otherwise uses the value typed into the comment form.', + `mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s e-mail address from the comment form, if user is anonymous, and the ’Anonymous users may/must leave their contact information’ setting is turned on.', + PRIMARY KEY (`id`,`node_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores comments and associated data.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_comments` +-- + +LOCK TABLES `qa_comments` WRITE; +/*!40000 ALTER TABLE `qa_comments` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_comments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_field_data` +-- + +DROP TABLE IF EXISTS `qa_field_data`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_field_data` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `field_id` int(11) NOT NULL, + `foreignKey` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `belongsTo` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `data` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `field_id` (`field_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_field_data` +-- + +LOCK TABLES `qa_field_data` WRITE; +/*!40000 ALTER TABLE `qa_field_data` DISABLE KEYS */; +INSERT INTO `qa_field_data` VALUES (1,1,'1','Node','

Content Boxes

\r\n

\r\n [content_box type=success]Maecenas pellentesque cursus auctor.[/content_box]

\r\n

\r\n [content_box type=error]Nam sagittis nisl non turpis aliquam mollis. Suspendisse ac metus nisi, sed vulputate arcu.[/content_box]

\r\n

\r\n [content_box type=alert]Cras interdum leo quis arcu sagittis pulvinar. Curabitur suscipit vulputate erat eu rhoncus. Morbi facilisis mi in ligula ornare ultricies.[/content_box]

\r\n

\r\n [content_box type=bubble]Fusce interdum cursus turpis vitae gravida. Aenean aliquet venenatis posuere. Etiam gravida ullamcorper purus.[/content_box]

\r\n
\r\n

\r\n Buttons

\r\n

\r\n Using buttons hookTags, you can easily create a variety of buttons. These buttons all stem from a single tag, but vary in color and size (each of which are adjustable using color=”" and size=”" parameters).
\r\n Allowed parameters:

\r\n
    \r\n
  1. \r\n size: big, small
  2. \r\n
  3. \r\n color:\r\n
      \r\n
    • \r\n small: black, blue, green, lightblue, orange, pink, purple, red, silver, teal
    • \r\n
    • \r\n big: blue, green, orange, purple, red, turquoise
    • \r\n
    \r\n
  4. \r\n
  5. \r\n link: url of your button
  6. \r\n
  7. \r\n target: open link en new window (_blank), open in same window (_self or unset parameter)
  8. \r\n
\r\n

\r\n  

\r\n

\r\n  

\r\n

\r\n Small Buttons

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n [button color=black]Button text[/button]\r\n [button color=blue]Button text[/button]
\r\n [button color=green]Button text[/button]\r\n [button color=lightblue]Button text[/button]
\r\n [button color=orange]Button text[/button]\r\n [button color=pink]Button text[/button]
\r\n [button color=purple]Button text[/button]\r\n [button color=red]Button text[/button]
\r\n [button color=silver]Button text[/button]\r\n [button color=teal]Button text[/button]
\r\n

\r\n  

\r\n

\r\n  

\r\n

\r\n Big Buttons

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n [button color=blue size=big]Button text[/button]\r\n [button color=green size=big]Button text[/button]
\r\n [button color=orange size=big]Button text[/button]\r\n [button color=purple size=big]Button text[/button]
\r\n [button color=red size=big]Button text[/button]\r\n [button color=turquoise size=big]Button text[/button]
\r\n

\r\n  

\r\n'),(2,1,'2','Node','Nam in iaculis lectus? Sed egestas dui quis leo porttitor vitae bibendum ipsum ultrices. Mauris nisi nulla, volutpat vel vestibulum non, lobortis sed lectus. Integer quis volutpat.'); +/*!40000 ALTER TABLE `qa_field_data` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_fields` +-- + +DROP TABLE IF EXISTS `qa_fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_fields` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a field', + `name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The name of this field. Must be unique', + `label` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Human name', + `belongsTo` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `field_module` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The module that implements the field object', + `description` text COLLATE utf8_unicode_ci, + `required` tinyint(1) NOT NULL DEFAULT '0', + `settings` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Rendering settings (View mode)', + `ordering` int(11) DEFAULT '1' COMMENT 'edit form ordering', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Fields instances'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_fields` +-- + +LOCK TABLES `qa_fields` WRITE; +/*!40000 ALTER TABLE `qa_fields` DISABLE KEYS */; +INSERT INTO `qa_fields` VALUES (1,'body','Body','NodeType-page','field_text','',1,'a:7:{s:7:\"display\";a:4:{s:7:\"default\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:1;s:11:\"trim_length\";s:3:\"180\";}s:4:\"full\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"600\";}s:4:\"list\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}s:3:\"rss\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}}s:4:\"type\";s:8:\"textarea\";s:11:\"text_format\";s:4:\"full\";s:7:\"max_len\";s:0:\"\";s:15:\"validation_rule\";s:0:\"\";s:18:\"validation_message\";s:0:\"\";s:15:\"text_processing\";s:4:\"full\";}',1); +/*!40000 ALTER TABLE `qa_fields` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_i18n` +-- + +DROP TABLE IF EXISTS `qa_i18n`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_i18n` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `locale` varchar(6) COLLATE utf8_unicode_ci NOT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `foreign_key` int(10) NOT NULL, + `field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `content` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `locale` (`locale`), + KEY `model` (`model`), + KEY `row_id` (`foreign_key`), + KEY `field` (`field`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_i18n` +-- + +LOCK TABLES `qa_i18n` WRITE; +/*!40000 ALTER TABLE `qa_i18n` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_i18n` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_languages` +-- + +DROP TABLE IF EXISTS `qa_languages`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_languages` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Language code, e.g. ’eng’', + `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Language name in English.', + `native` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Native language name.', + `direction` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'ltr' COMMENT 'Direction of language (Left-to-Right , Right-to-Left ).', + `icon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` int(11) NOT NULL DEFAULT '0' COMMENT 'Enabled flag (1 = Enabled, 0 = Disabled).', + `ordering` int(11) NOT NULL DEFAULT '0' COMMENT 'Weight, used in lists of languages.', + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all available languages in the system.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_languages` +-- + +LOCK TABLES `qa_languages` WRITE; +/*!40000 ALTER TABLE `qa_languages` DISABLE KEYS */; +INSERT INTO `qa_languages` VALUES (1,'eng','English','English','ltr','us.gif',1,0),(2,'spa','Spanish','Español','ltr','es.gif',1,0); +/*!40000 ALTER TABLE `qa_languages` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_menu_links` +-- + +DROP TABLE IF EXISTS `qa_menu_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_menu_links` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `menu_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The menu name. All links with the same menu name (such as ’navigation’) are part of the same menu.', + `lft` int(11) NOT NULL, + `rght` int(11) NOT NULL, + `parent_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', + `link_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'external path', + `router_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'internal path', + `description` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `link_title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The text displayed for the link, which may be modified by a title callback stored in menu_router.', + `options` text COLLATE utf8_unicode_ci COMMENT 'A serialized array of HTML attributes options.', + `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'system' COMMENT 'The name of the module that generated this link.', + `target` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT '_self', + `expanded` tinyint(6) NOT NULL DEFAULT '0' COMMENT 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)', + `selected_on` text COLLATE utf8_unicode_ci COMMENT 'php code, or regular expression. based on selected_on_type', + `selected_on_type` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'php = on php return TRUE; reg = on URL match', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `router_path` (`router_path`(128)), + KEY `menu_id` (`menu_id`) +) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Contains the individual links within a menu.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_menu_links` +-- + +LOCK TABLES `qa_menu_links` WRITE; +/*!40000 ALTER TABLE `qa_menu_links` DISABLE KEYS */; +INSERT INTO `qa_menu_links` VALUES (1,'management',1,2,0,NULL,'/admin/system/dashboard',NULL,'Dashboard',NULL,'system','_self',0,NULL,NULL,1),(2,'management',3,12,0,NULL,'/admin/system/structure',NULL,'Structure',NULL,'system','_self',0,NULL,NULL,1),(3,'management',13,14,0,NULL,'/admin/node/contents',NULL,'Content',NULL,'system','_self',0,NULL,NULL,1),(4,'management',15,16,0,NULL,'/admin/system/themes',NULL,'Appearance',NULL,'system','_self',0,NULL,NULL,1),(5,'management',17,18,0,NULL,'/admin/system/modules',NULL,'Modules',NULL,'system','_self',0,NULL,NULL,1),(6,'management',19,20,0,NULL,'/admin/user',NULL,'Users',NULL,'system','_self',0,NULL,NULL,1),(7,'management',23,24,0,NULL,'/admin/system/configuration',NULL,'Configuration',NULL,'system','_self',0,NULL,NULL,1),(8,'management',25,26,0,NULL,'/admin/system/help',NULL,'Help',NULL,'system','_self',0,NULL,NULL,1),(9,'management',4,5,2,NULL,'/admin/block','Configure what block content appears in your site\'s sidebars and other regions.','Blocks',NULL,'system','_self',0,NULL,NULL,1),(10,'management',6,7,2,NULL,'/admin/node/types','Manage content types.','Content Types',NULL,'system','_self',0,NULL,NULL,1),(11,'management',8,9,2,NULL,'/admin/menu','Add new menus to your site, edit existing menus, and rename and reorganize menu links.','Menus',NULL,'system','_self',0,NULL,NULL,1),(12,'management',10,11,2,NULL,'/admin/taxonomy','Manage tagging, categorization, and classification of your content.','Taxonomy',NULL,'system','_self',0,NULL,NULL,1),(13,'main-menu',3,4,0,NULL,'/d/hook-tags','','Hook Tags',NULL,'menu','_self',0,NULL,NULL,1),(17,'main-menu',5,6,0,NULL,'/d/about','','About',NULL,'menu','_self',0,NULL,NULL,1),(18,'management',21,22,0,NULL,'/admin/locale','','Languages',NULL,'locale','_self',0,NULL,NULL,1),(21,'main-menu',1,2,0,NULL,'/','','Home',NULL,'menu','_self',0,NULL,NULL,1),(22,'user-menu',1,2,0,NULL,'/user/my_account','','My account',NULL,'menu','_self',0,NULL,NULL,1),(23,'user-menu',3,4,0,NULL,'/user/logout','','Logout',NULL,'menu','_self',0,NULL,NULL,1); +/*!40000 ALTER TABLE `qa_menu_links` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_menus` +-- + +DROP TABLE IF EXISTS `qa_menus`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_menus` ( + `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.', + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Menu title; displayed at top of block.', + `description` text COLLATE utf8_unicode_ci COMMENT 'Menu description.', + `module` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_menus` +-- + +LOCK TABLES `qa_menus` WRITE; +/*!40000 ALTER TABLE `qa_menus` DISABLE KEYS */; +INSERT INTO `qa_menus` VALUES ('main-menu','Main menu','The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.','system'),('management','Management','The Management menu contains links for administrative tasks.','system'),('navigation','Navigation','The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.','system'),('user-menu','User menu','The User menu contains links related to the user\'s account, as well as the \'Log out\' link.','system'); +/*!40000 ALTER TABLE `qa_menus` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_modules` +-- + +DROP TABLE IF EXISTS `qa_modules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_modules` ( + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'machine name', + `type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'module' COMMENT 'module or theme', + `settings` text COLLATE utf8_unicode_ci COMMENT 'serialized extra data', + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_modules` +-- + +LOCK TABLES `qa_modules` WRITE; +/*!40000 ALTER TABLE `qa_modules` DISABLE KEYS */; +INSERT INTO `qa_modules` VALUES ('block','module','',1),('comment','module','',1),('field','module','',1),('locale','module','',1),('menu','module','',1),('node','module','',1),('system','module','',1),('taxonomy','module','',1),('theme_admin_default','theme','a:4:{s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"1\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";}',1),('theme_default','theme','a:7:{s:13:\"slider_folder\";s:6:\"slider\";s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"0\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";s:16:\"color_header_top\";s:7:\"#282727\";s:19:\"color_header_bottom\";s:7:\"#332f2f\";}',1),('user','module','',1); +/*!40000 ALTER TABLE `qa_modules` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_node_types` +-- + +DROP TABLE IF EXISTS `qa_node_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_node_types` ( + `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The machine-readable name of this type.', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The human-readable name of this type.', + `base` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The base string used to construct callbacks corresponding to this node type.', + `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The module defining this node type.', + `description` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'A brief description of this type.', + `title_label` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The label displayed for the title field on the edit form.', + `comments_approve` tinyint(1) DEFAULT '0', + `comments_per_page` int(4) NOT NULL DEFAULT '10', + `comments_anonymous` tinyint(3) NOT NULL DEFAULT '0', + `comments_subject_field` tinyint(1) NOT NULL DEFAULT '1', + `node_show_author` tinyint(1) DEFAULT '1', + `node_show_date` tinyint(1) DEFAULT '1', + `default_comment` int(11) DEFAULT NULL, + `default_language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `default_status` int(11) DEFAULT NULL, + `default_promote` int(11) DEFAULT NULL, + `default_sticky` int(11) DEFAULT NULL, + `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'A boolean indicating whether the node type is disabled.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores information about all defined node types.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_node_types` +-- + +LOCK TABLES `qa_node_types` WRITE; +/*!40000 ALTER TABLE `qa_node_types` DISABLE KEYS */; +INSERT INTO `qa_node_types` VALUES ('page','Basic page','node_content','system','Use basic pages for your static content, such as an \'About us\' page.','Title',1,10,2,1,0,0,0,'es',1,0,0,1); +/*!40000 ALTER TABLE `qa_node_types` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_nodes` +-- + +DROP TABLE IF EXISTS `qa_nodes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_nodes` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a node.', + `node_type_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The node_type.type of this node.', + `node_type_base` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'performance data for models', + `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The languages.language of this node.', + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The title of this node, always treated as non-markup plain text.', + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `slug` text COLLATE utf8_unicode_ci NOT NULL, + `terms_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', + `roles_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', + `created_by` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid that owns this node; initially, this is the user that created it.', + `status` int(11) NOT NULL DEFAULT '1' COMMENT 'Boolean indicating whether the node is published (visible to non-administrators).', + `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was created.', + `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was most recently saved.', + `modified_by` int(11) DEFAULT NULL, + `comment` int(11) NOT NULL DEFAULT '0' COMMENT 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).', + `comment_count` int(11) DEFAULT '0', + `promote` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed on the front page.', + `sticky` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.', + `cache` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `params` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`,`node_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='The base table for nodes.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_nodes` +-- + +LOCK TABLES `qa_nodes` WRITE; +/*!40000 ALTER TABLE `qa_nodes` DISABLE KEYS */; +INSERT INTO `qa_nodes` VALUES (1,'page','node_content','','Hook Tags','','hook-tags','','',1,1,1310424311,1310424311,1,0,0,0,0,'',NULL),(2,'page','node_content','','About','','about','','',1,1,1310424311,1310424311,1,0,1,1,0,'',NULL); +/*!40000 ALTER TABLE `qa_nodes` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_nodes_roles` +-- + +DROP TABLE IF EXISTS `qa_nodes_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_nodes_roles` ( + `node_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from roles.id.', + PRIMARY KEY (`node_id`,`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_nodes_roles` +-- + +LOCK TABLES `qa_nodes_roles` WRITE; +/*!40000 ALTER TABLE `qa_nodes_roles` DISABLE KEYS */; +INSERT INTO `qa_nodes_roles` VALUES ('1',0); +/*!40000 ALTER TABLE `qa_nodes_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_nodes_terms` +-- + +DROP TABLE IF EXISTS `qa_nodes_terms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_nodes_terms` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `node_id` int(20) NOT NULL DEFAULT '0', + `term_id` int(20) NOT NULL DEFAULT '0', + `field_id` int(11) NOT NULL DEFAULT '0' COMMENT 'field instance''s ID which creates this tag assoc.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_nodes_terms` +-- + +LOCK TABLES `qa_nodes_terms` WRITE; +/*!40000 ALTER TABLE `qa_nodes_terms` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_nodes_terms` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_roles` +-- + +DROP TABLE IF EXISTS `qa_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `ordering` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_roles` +-- + +LOCK TABLES `qa_roles` WRITE; +/*!40000 ALTER TABLE `qa_roles` DISABLE KEYS */; +INSERT INTO `qa_roles` VALUES (1,'administrator',1),(2,'authenticated user',2),(3,'anonymous user',3); +/*!40000 ALTER TABLE `qa_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_terms` +-- + +DROP TABLE IF EXISTS `qa_terms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_terms` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `vocabulary_id` int(11) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `modified` int(11) NOT NULL, + `created` int(11) NOT NULL, + `parent_id` int(11) DEFAULT '0', + `lft` int(11) NOT NULL, + `rght` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `slug` (`slug`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_terms` +-- + +LOCK TABLES `qa_terms` WRITE; +/*!40000 ALTER TABLE `qa_terms` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_terms` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_translations` +-- + +DROP TABLE IF EXISTS `qa_translations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_translations` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `original` text COLLATE utf8_unicode_ci NOT NULL, + `created` int(11) NOT NULL, + `created_by` int(11) NOT NULL, + `modified` int(11) NOT NULL, + `modified_by` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_translations` +-- + +LOCK TABLES `qa_translations` WRITE; +/*!40000 ALTER TABLE `qa_translations` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_translations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_users` +-- + +DROP TABLE IF EXISTS `qa_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(60) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `public_email` tinyint(1) NOT NULL DEFAULT '0', + `avatar` tinytext COLLATE utf8_unicode_ci COMMENT 'full url to avatar image file', + `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `timezone` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `key` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` tinyint(4) NOT NULL DEFAULT '0', + `created` int(11) NOT NULL, + `modified` int(11) NOT NULL, + `last_login` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_users` +-- + +LOCK TABLES `qa_users` WRITE; +/*!40000 ALTER TABLE `qa_users` DISABLE KEYS */; +INSERT INTO `qa_users` VALUES (1,'admin','QuickApps','f6ab52454037ee501824bf30e2fb0544edb36c77','info@quickapps.es',0,'','','','4e46f0b7-bb50-4587-9217-14bc22b50a39',1,1313271991,1313271991,1313773796); +/*!40000 ALTER TABLE `qa_users` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_users_roles` +-- + +DROP TABLE IF EXISTS `qa_users_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_users_roles` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `role_id` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='User HABTM Role'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_users_roles` +-- + +LOCK TABLES `qa_users_roles` WRITE; +/*!40000 ALTER TABLE `qa_users_roles` DISABLE KEYS */; +INSERT INTO `qa_users_roles` VALUES (8,1,1); +/*!40000 ALTER TABLE `qa_users_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_variables` +-- + +DROP TABLE IF EXISTS `qa_variables`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_variables` ( + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `value` text COLLATE utf8_unicode_ci, + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_variables` +-- + +LOCK TABLES `qa_variables` WRITE; +/*!40000 ALTER TABLE `qa_variables` DISABLE KEYS */; +INSERT INTO `qa_variables` VALUES ('admin_theme','s:12:\"AdminDefault\";'),('date_default_timezone','s:13:\"Europe/Madrid\";'),('default_language','s:3:\"eng\";'),('default_nodes_main','s:1:\"8\";'),('failed_login_limit','i:5;'),('rows_per_page','i:10;'),('site_description','a:0:{}'),('site_frontpage','a:0:{}'),('site_logo','s:8:\"logo.gif\";'),('site_mail','s:24:\"no-reply@your-domain.com\";'),('site_name','s:17:\"My QuickApps Site\";'),('site_online','s:1:\"1\";'),('site_slogan','s:142:\"Vivamus id feugiat ligula. Nulla facilisi. Integer lacus justo, elementum eget consequat a, molestie nec sapien. Quisque tincidunt, nunc vitae\";'),('site_theme','s:7:\"Default\";'),('user_default_avatar','s:25:\"/img/anonymous_avatar.jpg\";'),('user_mail_activation_body','s:246:\"[user_name],\r\n\r\nYour account at [site_name] has been activated.\r\n\r\nYou may now log in by clicking this link or copying and pasting it into your browser:\r\n\r\n[site_login_url]\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_activation_notify','s:1:\"1\";'),('user_mail_activation_subject','s:57:\"Account details for [user_name] at [site_name] (approved)\";'),('user_mail_blocked_body','s:85:\"[user_name],\r\n\r\nYour account on [site_name] has been blocked.\r\n\r\n-- [site_name] team\";'),('user_mail_blocked_notify','s:1:\"1\";'),('user_mail_blocked_subject','s:56:\"Account details for [user_name] at [site_name] (blocked)\";'),('user_mail_canceled_body','s:86:\"[user_name],\r\n\r\nYour account on [site_name] has been canceled.\r\n\r\n-- [site_name] team\";'),('user_mail_canceled_notify','s:1:\"1\";'),('user_mail_canceled_subject','s:57:\"Account details for [user_name] at [site_name] (canceled)\";'),('user_mail_password_recovery_body','s:273:\"[user_name],\r\n\r\nA request to reset the password for your account has been made at [site_name].\r\nYou may now log in by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nAfter log in you can reset your password.\r\n\r\n-- [site_name] team\";'),('user_mail_password_recovery_subject','s:60:\"Replacement login information for [user_name] at [site_name]\";'),('user_mail_welcome_body','s:301:\"[user_name],\r\n\r\nThank you for registering at [site_name]. You may now activate your account by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nThis link can only be used once to log in.\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_welcome_subject','s:46:\"Account details for [user_name] at [site_name]\";'); +/*!40000 ALTER TABLE `qa_variables` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `qa_vocabularies` +-- + +DROP TABLE IF EXISTS `qa_vocabularies`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `qa_vocabularies` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `required` tinyint(1) NOT NULL DEFAULT '0', + `ordering` int(11) DEFAULT '0', + `modified` int(11) NOT NULL, + `created` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `slug` (`slug`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `qa_vocabularies` +-- + +LOCK TABLES `qa_vocabularies` WRITE; +/*!40000 ALTER TABLE `qa_vocabularies` DISABLE KEYS */; +/*!40000 ALTER TABLE `qa_vocabularies` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-30 10:29:14 diff --git a/app/Config/Schema/tables/acos.sql b/app/Config/Schema/tables/acos.sql new file mode 100644 index 00000000..9bec64cc --- /dev/null +++ b/app/Config/Schema/tables/acos.sql @@ -0,0 +1,56 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__acos` +-- + +DROP TABLE IF EXISTS `#__acos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__acos` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `parent_id` int(10) DEFAULT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `foreign_key` int(10) DEFAULT NULL, + `alias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `lft` int(10) DEFAULT NULL, + `rght` int(10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=150 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__acos` +-- + +LOCK TABLES `#__acos` WRITE; +/*!40000 ALTER TABLE `#__acos` DISABLE KEYS */; +INSERT INTO `#__acos` VALUES (1,NULL,NULL,NULL,'Block',1,20),(2,1,NULL,NULL,'Block',2,5),(3,2,NULL,NULL,'admin_index',3,4),(4,1,NULL,NULL,'Manage',6,19),(5,4,NULL,NULL,'admin_index',7,8),(6,4,NULL,NULL,'admin_move',9,10),(7,4,NULL,NULL,'admin_clone',11,12),(8,4,NULL,NULL,'admin_edit',13,14),(9,4,NULL,NULL,'admin_add',15,16),(10,4,NULL,NULL,'admin_delete',17,18),(11,NULL,NULL,NULL,'Comment',21,34),(12,11,NULL,NULL,'Comment',22,25),(13,12,NULL,NULL,'admin_index',23,24),(14,11,NULL,NULL,'Published',26,29),(15,14,NULL,NULL,'admin_index',27,28),(16,11,NULL,NULL,'Unpublished',30,33),(17,16,NULL,NULL,'admin_index',31,32),(18,NULL,NULL,NULL,'Field',35,42),(19,18,NULL,NULL,'Handler',36,41),(20,19,NULL,NULL,'admin_delete',37,38),(21,19,NULL,NULL,'admin_move',39,40),(22,NULL,NULL,NULL,'FieldFile',43,50),(23,22,NULL,NULL,'Uploadify',44,49),(24,23,NULL,NULL,'delete',45,46),(25,23,NULL,NULL,'upload',47,48),(26,NULL,NULL,NULL,'Locale',51,90),(27,26,NULL,NULL,'Languages',52,63),(28,27,NULL,NULL,'admin_index',53,54),(29,27,NULL,NULL,'admin_set_default',55,56),(30,27,NULL,NULL,'admin_add',57,58),(31,27,NULL,NULL,'admin_edit',59,60),(32,27,NULL,NULL,'admin_delete',61,62),(33,26,NULL,NULL,'Locale',64,67),(34,33,NULL,NULL,'admin_index',65,66),(35,26,NULL,NULL,'Packages',68,77),(36,35,NULL,NULL,'admin_index',69,70),(37,35,NULL,NULL,'admin_download_package',71,72),(38,35,NULL,NULL,'admin_uninstall',73,74),(39,35,NULL,NULL,'admin_install',75,76),(40,26,NULL,NULL,'Translations',78,89),(41,40,NULL,NULL,'admin_index',79,80),(42,40,NULL,NULL,'admin_list',81,82),(43,40,NULL,NULL,'admin_edit',83,84),(44,40,NULL,NULL,'admin_add',85,86),(45,40,NULL,NULL,'admin_delete',87,88),(46,NULL,NULL,NULL,'Menu',91,114),(47,46,NULL,NULL,'Manage',92,109),(48,47,NULL,NULL,'admin_index',93,94),(49,47,NULL,NULL,'admin_delete',95,96),(50,47,NULL,NULL,'admin_add',97,98),(51,47,NULL,NULL,'admin_edit',99,100),(52,47,NULL,NULL,'admin_delete_link',101,102),(53,47,NULL,NULL,'admin_add_link',103,104),(54,47,NULL,NULL,'admin_edit_link',105,106),(55,47,NULL,NULL,'admin_links',107,108),(56,46,NULL,NULL,'Menu',110,113),(57,56,NULL,NULL,'admin_index',111,112),(58,NULL,NULL,NULL,'Node',115,158),(59,58,NULL,NULL,'Contents',116,129),(60,59,NULL,NULL,'admin_index',117,118),(61,59,NULL,NULL,'admin_edit',119,120),(62,59,NULL,NULL,'admin_create',121,122),(63,59,NULL,NULL,'admin_add',123,124),(64,59,NULL,NULL,'admin_delete',125,126),(65,59,NULL,NULL,'admin_clear_cache',127,128),(66,58,NULL,NULL,'Node',130,139),(67,66,NULL,NULL,'admin_index',131,132),(68,66,NULL,NULL,'index',133,134),(69,66,NULL,NULL,'details',135,136),(70,66,NULL,NULL,'search',137,138),(71,58,NULL,NULL,'Types',140,157),(72,71,NULL,NULL,'admin_index',141,142),(73,71,NULL,NULL,'admin_edit',143,144),(74,71,NULL,NULL,'admin_add',145,146),(75,71,NULL,NULL,'admin_delete',147,148),(76,71,NULL,NULL,'admin_display',149,150),(77,71,NULL,NULL,'admin_field_settings',151,152),(78,71,NULL,NULL,'admin_field_formatter',153,154),(79,71,NULL,NULL,'admin_fields',155,156),(80,NULL,NULL,NULL,'System',159,208),(81,80,NULL,NULL,'Configuration',160,163),(82,81,NULL,NULL,'admin_index',161,162),(83,80,NULL,NULL,'Dashboard',164,167),(84,83,NULL,NULL,'admin_index',165,166),(85,80,NULL,NULL,'Help',168,173),(86,85,NULL,NULL,'admin_index',169,170),(87,85,NULL,NULL,'admin_module',171,172),(88,80,NULL,NULL,'Modules',174,185),(89,88,NULL,NULL,'admin_index',175,176),(90,88,NULL,NULL,'admin_settings',177,178),(91,88,NULL,NULL,'admin_toggle',179,180),(92,88,NULL,NULL,'admin_uninstall',181,182),(93,88,NULL,NULL,'admin_install',183,184),(94,80,NULL,NULL,'Structure',186,189),(95,94,NULL,NULL,'admin_index',187,188),(96,80,NULL,NULL,'System',190,193),(97,96,NULL,NULL,'admin_index',191,192),(98,80,NULL,NULL,'Themes',194,207),(99,98,NULL,NULL,'admin_index',195,196),(100,98,NULL,NULL,'admin_set_theme',197,198),(101,98,NULL,NULL,'admin_settings',199,200),(102,98,NULL,NULL,'admin_uninstall',201,202),(103,98,NULL,NULL,'admin_install',203,204),(104,98,NULL,NULL,'admin_theme_tn',205,206),(105,NULL,NULL,NULL,'Taxonomy',209,232),(106,105,NULL,NULL,'Taxonomy',210,213),(107,106,NULL,NULL,'admin_index',211,212),(108,105,NULL,NULL,'Vocabularies',214,231),(109,108,NULL,NULL,'admin_index',215,216),(110,108,NULL,NULL,'admin_add',217,218),(111,108,NULL,NULL,'admin_move',219,220),(112,108,NULL,NULL,'admin_edit',221,222),(113,108,NULL,NULL,'admin_delete',223,224),(114,108,NULL,NULL,'admin_terms',225,226),(115,108,NULL,NULL,'admin_delete_term',227,228),(116,108,NULL,NULL,'admin_edit_term',229,230),(117,NULL,NULL,NULL,'User',233,298),(118,117,NULL,NULL,'Display',234,239),(119,118,NULL,NULL,'admin_index',235,236),(120,118,NULL,NULL,'admin_field_formatter',237,238),(121,117,NULL,NULL,'Fields',240,245),(122,121,NULL,NULL,'admin_index',241,242),(123,121,NULL,NULL,'admin_field_settings',243,244),(124,117,NULL,NULL,'List',246,259),(125,124,NULL,NULL,'admin_index',247,248),(126,124,NULL,NULL,'admin_delete',249,250),(127,124,NULL,NULL,'admin_block',251,252),(128,124,NULL,NULL,'admin_activate',253,254),(129,124,NULL,NULL,'admin_add',255,256),(130,124,NULL,NULL,'admin_edit',257,258),(131,117,NULL,NULL,'Permissions',260,267),(132,131,NULL,NULL,'admin_index',261,262),(133,131,NULL,NULL,'admin_edit',263,264),(134,131,NULL,NULL,'admin_toggle',265,266),(135,117,NULL,NULL,'Roles',268,275),(136,135,NULL,NULL,'admin_index',269,270),(137,135,NULL,NULL,'admin_edit',271,272),(138,135,NULL,NULL,'admin_delete',273,274),(139,117,NULL,NULL,'User',276,297),(140,139,NULL,NULL,'admin_index',277,278),(141,139,NULL,NULL,'login',279,280),(142,139,NULL,NULL,'logout',281,282),(143,139,NULL,NULL,'admin_login',283,284),(144,139,NULL,NULL,'admin_logout',285,286),(145,139,NULL,NULL,'register',287,288),(146,139,NULL,NULL,'activate',289,290),(147,139,NULL,NULL,'password_recovery',291,292),(148,139,NULL,NULL,'profile',293,294),(149,139,NULL,NULL,'my_account',295,296); +/*!40000 ALTER TABLE `#__acos` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-28 22:45:25 diff --git a/app/Config/Schema/tables/aros.sql b/app/Config/Schema/tables/aros.sql new file mode 100644 index 00000000..a8ee1529 --- /dev/null +++ b/app/Config/Schema/tables/aros.sql @@ -0,0 +1,56 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__aros` +-- + +DROP TABLE IF EXISTS `#__aros`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__aros` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `parent_id` int(10) DEFAULT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `foreign_key` int(10) DEFAULT NULL, + `alias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `lft` int(10) DEFAULT NULL, + `rght` int(10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__aros` +-- + +LOCK TABLES `#__aros` WRITE; +/*!40000 ALTER TABLE `#__aros` DISABLE KEYS */; +INSERT INTO `#__aros` VALUES (1,NULL,'User.Role',1,NULL,1,2),(2,NULL,'User.Role',2,NULL,3,4),(3,NULL,'User.Role',3,NULL,5,6); +/*!40000 ALTER TABLE `#__aros` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:45 diff --git a/app/Config/Schema/tables/aros_acos.sql b/app/Config/Schema/tables/aros_acos.sql new file mode 100644 index 00000000..94b20cf2 --- /dev/null +++ b/app/Config/Schema/tables/aros_acos.sql @@ -0,0 +1,56 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__aros_acos` +-- + +DROP TABLE IF EXISTS `#__aros_acos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__aros_acos` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `aro_id` int(10) NOT NULL, + `aco_id` int(10) NOT NULL, + `_create` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `_read` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `_update` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + `_delete` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__aros_acos` +-- + +LOCK TABLES `#__aros_acos` WRITE; +/*!40000 ALTER TABLE `#__aros_acos` DISABLE KEYS */; +INSERT INTO `#__aros_acos` VALUES (1,3,68,'1','1','1','1'),(2,2,68,'1','1','1','1'),(3,3,69,'1','1','1','1'),(4,2,69,'1','1','1','1'),(5,3,70,'1','1','1','1'),(6,2,70,'1','1','1','1'),(7,3,141,'1','1','1','1'),(8,2,142,'1','1','1','1'),(9,3,143,'1','1','1','1'),(10,2,144,'1','1','1','1'),(11,3,145,'1','1','1','1'),(12,3,146,'1','1','1','1'),(13,2,146,'1','1','1','1'),(14,2,147,'1','1','1','1'),(15,3,147,'1','1','1','1'),(16,2,148,'1','1','1','1'),(17,2,149,'1','1','1','1'); +/*!40000 ALTER TABLE `#__aros_acos` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-28 22:45:25 diff --git a/app/Config/Schema/tables/block_custom.sql b/app/Config/Schema/tables/block_custom.sql new file mode 100644 index 00000000..b7477216 --- /dev/null +++ b/app/Config/Schema/tables/block_custom.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__block_custom` +-- + +DROP TABLE IF EXISTS `#__block_custom`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__block_custom` ( + `block_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The block’s block.bid.', + `body` longtext COLLATE utf8_unicode_ci COMMENT 'Block contents.', + `description` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Block description.', + `format` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The filter_format.format of the block body.', + PRIMARY KEY (`block_id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores contents of custom-made blocks.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__block_custom` +-- + +LOCK TABLES `#__block_custom` WRITE; +/*!40000 ALTER TABLE `#__block_custom` DISABLE KEYS */; +INSERT INTO `#__block_custom` VALUES (11,'

Duis tellus nunc, egestas a interdum sed, congue vitae magna. Curabitur a tellus quis lacus blandit sagittis a sit amet elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas metus sed neque ultricies.\r\n[button size=small color=silver]Read more[/button]

\r\n','Services-LEFT',NULL),(12,'

Integer egestas ultricies urna vitae molestie. Donec nec facilisis nisi. Vivamus tempor feugiat velit gravida vehicula. Donec faucibus pellentesque ipsum id varius. Ut rutrum metus sed neque ultricies a dictum ante sagittis.\r\n[button size=small color=silver]Read more[/button]

\r\n','Services-CENTER',NULL),(13,'

Praesent et metus sit amet nisl luctus commodo ut a risus. Mauris vehicula, ligula quis consectetur feugiat, arcu nibh tempor nisi, at varius dolor dolor nec dolor. Donec auctor mi vitae neque. Praesent sollicitudin egestas felis vitae gravida.\r\n[button size=small color=silver]Read more[/button]\r\n

\r\n','Services-RIGHT',NULL); +/*!40000 ALTER TABLE `#__block_custom` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:45 diff --git a/app/Config/Schema/tables/block_regions.sql b/app/Config/Schema/tables/block_regions.sql new file mode 100644 index 00000000..ab06cb09 --- /dev/null +++ b/app/Config/Schema/tables/block_regions.sql @@ -0,0 +1,54 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__block_regions` +-- + +DROP TABLE IF EXISTS `#__block_regions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__block_regions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `block_id` int(11) NOT NULL, + `theme` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `region` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `ordering` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`,`block_id`) +) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__block_regions` +-- + +LOCK TABLES `#__block_regions` WRITE; +/*!40000 ALTER TABLE `#__block_regions` DISABLE KEYS */; +INSERT INTO `#__block_regions` VALUES (8,10,'AdminDefault','dashboard_main',1),(9,10,'Default','dashboard_main',1),(13,4,'AdminDefault','management-menu',1),(14,4,'Default','management-menu',1),(18,3,'AdminDefault','footer',1),(48,3,'Default','footer',1),(131,6,'Default','main-menu',1),(133,26,'Default','slider',1),(140,9,'Default','language-switcher',1),(151,19,'Default','services-left',1),(153,20,'Default','services-center',1),(155,25,'Default','services-right',1),(157,15,'Default','search',1),(159,12,'Default','services-left',2),(161,11,'Default','services-center',2),(163,13,'Default','services-right',2),(165,14,'Default','slider',2),(166,15,'AdminDefault','dashboard_sidebar',1),(169,7,'AdminDefault','dashboard_sidebar',2),(172,5,'Default','user-menu',2); +/*!40000 ALTER TABLE `#__block_regions` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/block_roles.sql b/app/Config/Schema/tables/block_roles.sql new file mode 100644 index 00000000..d2447aa3 --- /dev/null +++ b/app/Config/Schema/tables/block_roles.sql @@ -0,0 +1,51 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__block_roles` +-- + +DROP TABLE IF EXISTS `#__block_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__block_roles` ( + `block_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `user_role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from users_roles.rid.', + PRIMARY KEY (`block_id`,`user_role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__block_roles` +-- + +LOCK TABLES `#__block_roles` WRITE; +/*!40000 ALTER TABLE `#__block_roles` DISABLE KEYS */; +INSERT INTO `#__block_roles` VALUES ('1',3),('5',2); +/*!40000 ALTER TABLE `#__block_roles` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/blocks.sql b/app/Config/Schema/tables/blocks.sql new file mode 100644 index 00000000..623c5940 --- /dev/null +++ b/app/Config/Schema/tables/blocks.sql @@ -0,0 +1,62 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__blocks` +-- + +DROP TABLE IF EXISTS `#__blocks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__blocks` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key - Unique block ID.', + `module` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The module from which the block originates; for example, ’user’ for the Who’s Online block, and ’block’ for any custom blocks.', + `delta` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'Unique ID for block within a module. Or menu_id', + `clone_of` int(11) NOT NULL DEFAULT '0', + `themes_cache` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'store all themes that belongs to (see block_regions table)', + `ordering` int(11) NOT NULL DEFAULT '1', + `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT 'Block enabled status. (1 = enabled, 0 = disabled)', + `visibility` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Flag to indicate how to show blocks on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)', + `pages` text COLLATE utf8_unicode_ci COMMENT 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.', + `title` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)', + `locale` text COLLATE utf8_unicode_ci, + `settings` text COLLATE utf8_unicode_ci, + `params` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores block settings, such as region and visibility...'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__blocks` +-- + +LOCK TABLES `#__blocks` WRITE; +/*!40000 ALTER TABLE `#__blocks` DISABLE KEYS */; +INSERT INTO `#__blocks` VALUES (1,'user','login',0,'',1,0,0,'','User Login','a:0:{}','',NULL),(2,'menu','navigation',0,':Default:',2,1,0,'','',NULL,'',NULL),(3,'system','powered_by',0,':AdminDefault:Default:',1,1,0,'','Powered By','a:0:{}','',NULL),(4,'menu','management',0,':AdminDefault:',1,1,1,'/admin/*','','a:0:{}','',NULL),(5,'menu','user-menu',0,':Default:',4,1,0,'','User Menu','a:0:{}','',NULL),(6,'menu','main-menu',0,':Default:',1,1,0,'','','a:0:{}','',NULL),(7,'user','new',0,':AdminDefault:',5,1,0,'','New Users','a:0:{}','a:1:{s:10:\"show_limit\";s:1:\"5\";}',NULL),(9,'locale','language_switcher',0,':Default:',3,1,0,'','Language switcher','a:0:{}','a:2:{s:5:\"flags\";s:1:\"1\";s:4:\"name\";s:1:\"1\";}',NULL),(10,'system','recent_content',0,':AdminDefault:',1,1,0,'','Updates','a:0:{}','',NULL),(11,'block','5',0,':Default:',1,1,0,'','WHAT WE DO','a:0:{}','',NULL),(12,'block','6',0,':Default:',1,1,0,'','OUR MISSION','a:0:{}','',NULL),(13,'block','7',0,':Default:',1,1,0,'','WHO WE ARE','a:0:{}','',NULL),(14,'theme_default','slider',0,':Default:',1,1,1,'/','Slider','a:0:{}','a:1:{s:12:\"slider_order\";s:52:\"1_[language].jpg\r\n2_[language].jpg\r\n3_[language].jpg\";}',NULL),(15,'node','search',0,':AdminDefault:Default:',1,1,0,'','Search','a:0:{}','',NULL),(16,'taxonomy','vocabularies',0,NULL,1,1,0,NULL,'Vocabularies',NULL,NULL,NULL); +/*!40000 ALTER TABLE `#__blocks` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-13 16:46:05 diff --git a/app/Config/Schema/tables/comments.sql b/app/Config/Schema/tables/comments.sql new file mode 100644 index 00000000..9454a550 --- /dev/null +++ b/app/Config/Schema/tables/comments.sql @@ -0,0 +1,60 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__comments` +-- + +DROP TABLE IF EXISTS `#__comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__comments` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key: Unique comment ID.', + `node_id` int(11) NOT NULL COMMENT 'The node.nid to which this comment is a reply.', + `user_id` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid who authored the comment. If set to 0, this comment was created by an anonymous user.', + `body` text COLLATE utf8_unicode_ci, + `subject` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `hostname` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The author’s host name. (IP)', + `homepage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was created, as a Unix timestamp.', + `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was last edited, as a Unix timestamp.', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT 'The published status of a comment. (0 = Not Published, 1 = Published)', + `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s name. Uses users.name if the user is logged in, otherwise uses the value typed into the comment form.', + `mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s e-mail address from the comment form, if user is anonymous, and the ’Anonymous users may/must leave their contact information’ setting is turned on.', + PRIMARY KEY (`id`,`node_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores comments and associated data.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__comments` +-- + +LOCK TABLES `#__comments` WRITE; +/*!40000 ALTER TABLE `#__comments` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__comments` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/field_data.sql b/app/Config/Schema/tables/field_data.sql new file mode 100644 index 00000000..2d688082 --- /dev/null +++ b/app/Config/Schema/tables/field_data.sql @@ -0,0 +1,55 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__field_data` +-- + +DROP TABLE IF EXISTS `#__field_data`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__field_data` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `field_id` int(11) NOT NULL, + `foreignKey` varchar(20) COLLATE utf8_unicode_ci NOT NULL, + `belongsTo` varchar(100) COLLATE utf8_unicode_ci NOT NULL, + `data` longtext COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `field_id` (`field_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__field_data` +-- + +LOCK TABLES `#__field_data` WRITE; +/*!40000 ALTER TABLE `#__field_data` DISABLE KEYS */; +INSERT INTO `#__field_data` VALUES (1,1,'1','Node','

Content Boxes

\r\n

\r\n [content_box type=success]Maecenas pellentesque cursus auctor.[/content_box]

\r\n

\r\n [content_box type=error]Nam sagittis nisl non turpis aliquam mollis. Suspendisse ac metus nisi, sed vulputate arcu.[/content_box]

\r\n

\r\n [content_box type=alert]Cras interdum leo quis arcu sagittis pulvinar. Curabitur suscipit vulputate erat eu rhoncus. Morbi facilisis mi in ligula ornare ultricies.[/content_box]

\r\n

\r\n [content_box type=bubble]Fusce interdum cursus turpis vitae gravida. Aenean aliquet venenatis posuere. Etiam gravida ullamcorper purus.[/content_box]

\r\n
\r\n

\r\n Buttons

\r\n

\r\n Using buttons hookTags, you can easily create a variety of buttons. These buttons all stem from a single tag, but vary in color and size (each of which are adjustable using color=”" and size=”" parameters).
\r\n Allowed parameters:

\r\n
    \r\n
  1. \r\n size: big, small
  2. \r\n
  3. \r\n color:\r\n
      \r\n
    • \r\n small: black, blue, green, lightblue, orange, pink, purple, red, silver, teal
    • \r\n
    • \r\n big: blue, green, orange, purple, red, turquoise
    • \r\n
    \r\n
  4. \r\n
  5. \r\n link: url of your button
  6. \r\n
  7. \r\n target: open link en new window (_blank), open in same window (_self or unset parameter)
  8. \r\n
\r\n

\r\n  

\r\n

\r\n  

\r\n

\r\n Small Buttons

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n [button color=black]Button text[/button]\r\n [button color=blue]Button text[/button]
\r\n [button color=green]Button text[/button]\r\n [button color=lightblue]Button text[/button]
\r\n [button color=orange]Button text[/button]\r\n [button color=pink]Button text[/button]
\r\n [button color=purple]Button text[/button]\r\n [button color=red]Button text[/button]
\r\n [button color=silver]Button text[/button]\r\n [button color=teal]Button text[/button]
\r\n

\r\n  

\r\n

\r\n  

\r\n

\r\n Big Buttons

\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n [button color=blue size=big]Button text[/button]\r\n [button color=green size=big]Button text[/button]
\r\n [button color=orange size=big]Button text[/button]\r\n [button color=purple size=big]Button text[/button]
\r\n [button color=red size=big]Button text[/button]\r\n [button color=turquoise size=big]Button text[/button]
\r\n

\r\n  

\r\n'),(2,1,'2','Node','Nam in iaculis lectus? Sed egestas dui quis leo porttitor vitae bibendum ipsum ultrices. Mauris nisi nulla, volutpat vel vestibulum non, lobortis sed lectus. Integer quis volutpat.'); +/*!40000 ALTER TABLE `#__field_data` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-02 12:04:02 diff --git a/app/Config/Schema/tables/fields.sql b/app/Config/Schema/tables/fields.sql new file mode 100644 index 00000000..384204a6 --- /dev/null +++ b/app/Config/Schema/tables/fields.sql @@ -0,0 +1,58 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__fields` +-- + +DROP TABLE IF EXISTS `#__fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__fields` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a field', + `name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The name of this field. Must be unique', + `label` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Human name', + `belongsTo` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, + `field_module` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The module that implements the field object', + `description` text COLLATE utf8_unicode_ci, + `required` tinyint(1) NOT NULL DEFAULT '0', + `settings` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Rendering settings (View mode)', + `ordering` int(11) DEFAULT '1' COMMENT 'edit form ordering', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Fields instances'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__fields` +-- + +LOCK TABLES `#__fields` WRITE; +/*!40000 ALTER TABLE `#__fields` DISABLE KEYS */; +INSERT INTO `#__fields` VALUES (1,'body','Body','NodeType-page','field_text','',1,'a:7:{s:7:\"display\";a:4:{s:7:\"default\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:1;s:11:\"trim_length\";s:3:\"180\";}s:4:\"full\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"600\";}s:4:\"list\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}s:3:\"rss\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}}s:4:\"type\";s:8:\"textarea\";s:11:\"text_format\";s:4:\"full\";s:7:\"max_len\";s:0:\"\";s:15:\"validation_rule\";s:0:\"\";s:18:\"validation_message\";s:0:\"\";s:15:\"text_processing\";s:4:\"full\";}',1); +/*!40000 ALTER TABLE `#__fields` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/i18n.sql b/app/Config/Schema/tables/i18n.sql new file mode 100644 index 00000000..f3c8c0c4 --- /dev/null +++ b/app/Config/Schema/tables/i18n.sql @@ -0,0 +1,58 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__i18n` +-- + +DROP TABLE IF EXISTS `#__i18n`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__i18n` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `locale` varchar(6) COLLATE utf8_unicode_ci NOT NULL, + `model` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `foreign_key` int(10) NOT NULL, + `field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `content` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`), + KEY `locale` (`locale`), + KEY `model` (`model`), + KEY `row_id` (`foreign_key`), + KEY `field` (`field`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__i18n` +-- + +LOCK TABLES `#__i18n` WRITE; +/*!40000 ALTER TABLE `#__i18n` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__i18n` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-02 12:11:33 diff --git a/app/Config/Schema/tables/languages.sql b/app/Config/Schema/tables/languages.sql new file mode 100644 index 00000000..790bdd00 --- /dev/null +++ b/app/Config/Schema/tables/languages.sql @@ -0,0 +1,58 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__languages` +-- + +DROP TABLE IF EXISTS `#__languages`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__languages` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Language code, e.g. ’eng’', + `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Language name in English.', + `native` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Native language name.', + `direction` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'ltr' COMMENT 'Direction of language (Left-to-Right , Right-to-Left ).', + `icon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` int(11) NOT NULL DEFAULT '0' COMMENT 'Enabled flag (1 = Enabled, 0 = Disabled).', + `ordering` int(11) NOT NULL DEFAULT '0' COMMENT 'Weight, used in lists of languages.', + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all available languages in the system.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__languages` +-- + +LOCK TABLES `#__languages` WRITE; +/*!40000 ALTER TABLE `#__languages` DISABLE KEYS */; +INSERT INTO `#__languages` VALUES (1,'eng','English','English','ltr','us.gif',1,0),(2,'spa','Spanish','Español','ltr','es.gif',1,0); +/*!40000 ALTER TABLE `#__languages` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/menu_links.sql b/app/Config/Schema/tables/menu_links.sql new file mode 100644 index 00000000..ed8fcd3d --- /dev/null +++ b/app/Config/Schema/tables/menu_links.sql @@ -0,0 +1,67 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__menu_links` +-- + +DROP TABLE IF EXISTS `#__menu_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__menu_links` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `menu_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The menu name. All links with the same menu name (such as ’navigation’) are part of the same menu.', + `lft` int(11) NOT NULL, + `rght` int(11) NOT NULL, + `parent_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', + `link_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'external path', + `router_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'internal path', + `description` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, + `link_title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The text displayed for the link, which may be modified by a title callback stored in menu_router.', + `options` text COLLATE utf8_unicode_ci COMMENT 'A serialized array of HTML attributes options.', + `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'system' COMMENT 'The name of the module that generated this link.', + `target` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT '_self', + `expanded` tinyint(6) NOT NULL DEFAULT '0' COMMENT 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)', + `selected_on` text COLLATE utf8_unicode_ci COMMENT 'php code, or regular expression. based on selected_on_type', + `selected_on_type` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'php = on php return TRUE; reg = on URL match', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `router_path` (`router_path`(128)), + KEY `menu_id` (`menu_id`) +) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Contains the individual links within a menu.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__menu_links` +-- + +LOCK TABLES `#__menu_links` WRITE; +/*!40000 ALTER TABLE `#__menu_links` DISABLE KEYS */; +INSERT INTO `#__menu_links` VALUES (1,'management',1,2,0,NULL,'/admin/system/dashboard',NULL,'Dashboard',NULL,'system','_self',0,NULL,NULL,1),(2,'management',3,12,0,NULL,'/admin/system/structure',NULL,'Structure',NULL,'system','_self',0,NULL,NULL,1),(3,'management',13,14,0,NULL,'/admin/node/contents',NULL,'Content',NULL,'system','_self',0,NULL,NULL,1),(4,'management',15,16,0,NULL,'/admin/system/themes',NULL,'Appearance',NULL,'system','_self',0,NULL,NULL,1),(5,'management',17,18,0,NULL,'/admin/system/modules',NULL,'Modules',NULL,'system','_self',0,NULL,NULL,1),(6,'management',19,20,0,NULL,'/admin/user',NULL,'Users',NULL,'system','_self',0,NULL,NULL,1),(7,'management',23,24,0,NULL,'/admin/system/configuration',NULL,'Configuration',NULL,'system','_self',0,NULL,NULL,1),(8,'management',25,26,0,NULL,'/admin/system/help',NULL,'Help',NULL,'system','_self',0,NULL,NULL,1),(9,'management',4,5,2,NULL,'/admin/block','Configure what block content appears in your site\'s sidebars and other regions.','Blocks',NULL,'system','_self',0,NULL,NULL,1),(10,'management',6,7,2,NULL,'/admin/node/types','Manage content types.','Content Types',NULL,'system','_self',0,NULL,NULL,1),(11,'management',8,9,2,NULL,'/admin/menu','Add new menus to your site, edit existing menus, and rename and reorganize menu links.','Menus',NULL,'system','_self',0,NULL,NULL,1),(12,'management',10,11,2,NULL,'/admin/taxonomy','Manage tagging, categorization, and classification of your content.','Taxonomy',NULL,'system','_self',0,NULL,NULL,1),(13,'main-menu',3,4,0,NULL,'/d/hook-tags','','Hook Tags',NULL,'menu','_self',0,NULL,NULL,1),(17,'main-menu',5,6,0,NULL,'/d/about','','About',NULL,'menu','_self',0,NULL,NULL,1),(18,'management',21,22,0,NULL,'/admin/locale','','Languages',NULL,'locale','_self',0,NULL,NULL,1),(21,'main-menu',1,2,0,NULL,'/','','Home',NULL,'menu','_self',0,NULL,NULL,1),(22,'user-menu',1,2,0,NULL,'/user/my_account','','My account',NULL,'menu','_self',0,NULL,NULL,1),(23,'user-menu',3,4,0,NULL,'/user/logout','','Logout',NULL,'menu','_self',0,NULL,NULL,1); +/*!40000 ALTER TABLE `#__menu_links` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-30 10:29:15 diff --git a/app/Config/Schema/tables/menus.sql b/app/Config/Schema/tables/menus.sql new file mode 100644 index 00000000..202c4666 --- /dev/null +++ b/app/Config/Schema/tables/menus.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__menus` +-- + +DROP TABLE IF EXISTS `#__menus`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__menus` ( + `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.', + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Menu title; displayed at top of block.', + `description` text COLLATE utf8_unicode_ci COMMENT 'Menu description.', + `module` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__menus` +-- + +LOCK TABLES `#__menus` WRITE; +/*!40000 ALTER TABLE `#__menus` DISABLE KEYS */; +INSERT INTO `#__menus` VALUES ('main-menu','Main menu','The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.','system'),('management','Management','The Management menu contains links for administrative tasks.','system'),('navigation','Navigation','The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.','system'),('user-menu','User menu','The User menu contains links related to the user\'s account, as well as the \'Log out\' link.','system'); +/*!40000 ALTER TABLE `#__menus` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-12 13:32:32 diff --git a/app/Config/Schema/tables/modules.sql b/app/Config/Schema/tables/modules.sql new file mode 100644 index 00000000..a0182f29 --- /dev/null +++ b/app/Config/Schema/tables/modules.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__modules` +-- + +DROP TABLE IF EXISTS `#__modules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__modules` ( + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'machine name', + `type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'module' COMMENT 'module or theme', + `settings` text COLLATE utf8_unicode_ci COMMENT 'serialized extra data', + `status` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__modules` +-- + +LOCK TABLES `#__modules` WRITE; +/*!40000 ALTER TABLE `#__modules` DISABLE KEYS */; +INSERT INTO `#__modules` VALUES ('block','module','',1),('comment','module','',1),('field','module','',1),('locale','module','',1),('menu','module','',1),('node','module','',1),('system','module','',1),('taxonomy','module','',1),('theme_admin_default','theme','a:4:{s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"1\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";}',1),('theme_default','theme','a:7:{s:13:\"slider_folder\";s:6:\"slider\";s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"0\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";s:16:\"color_header_top\";s:7:\"#282727\";s:19:\"color_header_bottom\";s:7:\"#332f2f\";}',1),('user','module','',1); +/*!40000 ALTER TABLE `#__modules` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/node_types.sql b/app/Config/Schema/tables/node_types.sql new file mode 100644 index 00000000..8bc95c77 --- /dev/null +++ b/app/Config/Schema/tables/node_types.sql @@ -0,0 +1,67 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__node_types` +-- + +DROP TABLE IF EXISTS `#__node_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__node_types` ( + `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The machine-readable name of this type.', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The human-readable name of this type.', + `base` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The base string used to construct callbacks corresponding to this node type.', + `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The module defining this node type.', + `description` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'A brief description of this type.', + `title_label` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The label displayed for the title field on the edit form.', + `comments_approve` tinyint(1) DEFAULT '0', + `comments_per_page` int(4) NOT NULL DEFAULT '10', + `comments_anonymous` tinyint(3) NOT NULL DEFAULT '0', + `comments_subject_field` tinyint(1) NOT NULL DEFAULT '1', + `node_show_author` tinyint(1) DEFAULT '1', + `node_show_date` tinyint(1) DEFAULT '1', + `default_comment` int(11) DEFAULT NULL, + `default_language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `default_status` int(11) DEFAULT NULL, + `default_promote` int(11) DEFAULT NULL, + `default_sticky` int(11) DEFAULT NULL, + `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'A boolean indicating whether the node type is disabled.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores information about all defined node types.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__node_types` +-- + +LOCK TABLES `#__node_types` WRITE; +/*!40000 ALTER TABLE `#__node_types` DISABLE KEYS */; +INSERT INTO `#__node_types` VALUES ('page','Basic page','node_content','system','Use basic pages for your static content, such as an \'About us\' page.','Title',1,10,2,1,0,0,0,'es',1,0,0,1); +/*!40000 ALTER TABLE `#__node_types` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/nodes.sql b/app/Config/Schema/tables/nodes.sql new file mode 100644 index 00000000..037ea8b2 --- /dev/null +++ b/app/Config/Schema/tables/nodes.sql @@ -0,0 +1,69 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__nodes` +-- + +DROP TABLE IF EXISTS `#__nodes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__nodes` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a node.', + `node_type_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The node_type.type of this node.', + `node_type_base` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'performance data for models', + `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The languages.language of this node.', + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The title of this node, always treated as non-markup plain text.', + `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `slug` text COLLATE utf8_unicode_ci NOT NULL, + `terms_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', + `roles_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', + `created_by` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid that owns this node; initially, this is the user that created it.', + `status` int(11) NOT NULL DEFAULT '1' COMMENT 'Boolean indicating whether the node is published (visible to non-administrators).', + `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was created.', + `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was most recently saved.', + `modified_by` int(11) DEFAULT NULL, + `comment` int(11) NOT NULL DEFAULT '0' COMMENT 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).', + `comment_count` int(11) DEFAULT '0', + `promote` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed on the front page.', + `sticky` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.', + `cache` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, + `params` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`id`,`node_type_id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='The base table for nodes.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__nodes` +-- + +LOCK TABLES `#__nodes` WRITE; +/*!40000 ALTER TABLE `#__nodes` DISABLE KEYS */; +INSERT INTO `#__nodes` VALUES (1,'page','node_content','','Hook Tags','','hook-tags','','',1,1,1310424311,1310424311,1,0,0,0,0,'',NULL),(2,'page','node_content','','About','','about','','',1,1,1310424311,1310424311,1,0,1,1,0,'',NULL); +/*!40000 ALTER TABLE `#__nodes` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-09-13 16:46:05 diff --git a/app/Config/Schema/tables/nodes_roles.sql b/app/Config/Schema/tables/nodes_roles.sql new file mode 100644 index 00000000..039a67e5 --- /dev/null +++ b/app/Config/Schema/tables/nodes_roles.sql @@ -0,0 +1,51 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__nodes_roles` +-- + +DROP TABLE IF EXISTS `#__nodes_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__nodes_roles` ( + `node_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, + `role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from roles.id.', + PRIMARY KEY (`node_id`,`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__nodes_roles` +-- + +LOCK TABLES `#__nodes_roles` WRITE; +/*!40000 ALTER TABLE `#__nodes_roles` DISABLE KEYS */; +INSERT INTO `#__nodes_roles` VALUES ('1',0); +/*!40000 ALTER TABLE `#__nodes_roles` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/nodes_terms.sql b/app/Config/Schema/tables/nodes_terms.sql new file mode 100644 index 00000000..cbb61fb1 --- /dev/null +++ b/app/Config/Schema/tables/nodes_terms.sql @@ -0,0 +1,52 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__nodes_terms` +-- + +DROP TABLE IF EXISTS `#__nodes_terms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__nodes_terms` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `node_id` int(20) NOT NULL DEFAULT '0', + `term_id` int(20) NOT NULL DEFAULT '0', + `field_id` int(11) NOT NULL DEFAULT '0' COMMENT 'field instance''s ID which creates this tag assoc.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__nodes_terms` +-- + +LOCK TABLES `#__nodes_terms` WRITE; +/*!40000 ALTER TABLE `#__nodes_terms` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__nodes_terms` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/roles.sql b/app/Config/Schema/tables/roles.sql new file mode 100644 index 00000000..41f6047c --- /dev/null +++ b/app/Config/Schema/tables/roles.sql @@ -0,0 +1,52 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__roles` +-- + +DROP TABLE IF EXISTS `#__roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `ordering` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__roles` +-- + +LOCK TABLES `#__roles` WRITE; +/*!40000 ALTER TABLE `#__roles` DISABLE KEYS */; +INSERT INTO `#__roles` VALUES (1,'administrator',1),(2,'authenticated user',2),(3,'anonymous user',3); +/*!40000 ALTER TABLE `#__roles` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/terms.sql b/app/Config/Schema/tables/terms.sql new file mode 100644 index 00000000..dbec109b --- /dev/null +++ b/app/Config/Schema/tables/terms.sql @@ -0,0 +1,59 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__terms` +-- + +DROP TABLE IF EXISTS `#__terms`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__terms` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `vocabulary_id` int(11) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `modified` int(11) NOT NULL, + `created` int(11) NOT NULL, + `parent_id` int(11) DEFAULT '0', + `lft` int(11) NOT NULL, + `rght` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `slug` (`slug`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__terms` +-- + +LOCK TABLES `#__terms` WRITE; +/*!40000 ALTER TABLE `#__terms` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__terms` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/translations.sql b/app/Config/Schema/tables/translations.sql new file mode 100644 index 00000000..b567da81 --- /dev/null +++ b/app/Config/Schema/tables/translations.sql @@ -0,0 +1,54 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__translations` +-- + +DROP TABLE IF EXISTS `#__translations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__translations` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `original` text COLLATE utf8_unicode_ci NOT NULL, + `created` int(11) NOT NULL, + `created_by` int(11) NOT NULL, + `modified` int(11) NOT NULL, + `modified_by` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__translations` +-- + +LOCK TABLES `#__translations` WRITE; +/*!40000 ALTER TABLE `#__translations` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__translations` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/users.sql b/app/Config/Schema/tables/users.sql new file mode 100644 index 00000000..17ac6a02 --- /dev/null +++ b/app/Config/Schema/tables/users.sql @@ -0,0 +1,57 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__users` +-- + +DROP TABLE IF EXISTS `#__users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(60) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `public_email` tinyint(1) NOT NULL DEFAULT '0', + `avatar` tinytext COLLATE utf8_unicode_ci COMMENT 'full url to avatar image file', + `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, + `timezone` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `key` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` tinyint(4) NOT NULL DEFAULT '0', + `created` int(11) NOT NULL, + `modified` int(11) NOT NULL, + `last_login` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__users` +-- + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-30 1:37:16 diff --git a/app/Config/Schema/tables/users_roles.sql b/app/Config/Schema/tables/users_roles.sql new file mode 100644 index 00000000..05ede736 --- /dev/null +++ b/app/Config/Schema/tables/users_roles.sql @@ -0,0 +1,52 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__users_roles` +-- + +DROP TABLE IF EXISTS `#__users_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__users_roles` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `role_id` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='User HABTM Role'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__users_roles` +-- + +LOCK TABLES `#__users_roles` WRITE; +/*!40000 ALTER TABLE `#__users_roles` DISABLE KEYS */; +INSERT INTO `#__users_roles` VALUES (8,1,1); +/*!40000 ALTER TABLE `#__users_roles` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/variables.sql b/app/Config/Schema/tables/variables.sql new file mode 100644 index 00000000..48d3180b --- /dev/null +++ b/app/Config/Schema/tables/variables.sql @@ -0,0 +1,51 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__variables` +-- + +DROP TABLE IF EXISTS `#__variables`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__variables` ( + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `value` text COLLATE utf8_unicode_ci, + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__variables` +-- + +LOCK TABLES `#__variables` WRITE; +/*!40000 ALTER TABLE `#__variables` DISABLE KEYS */; +INSERT INTO `#__variables` VALUES ('admin_theme','s:12:\"AdminDefault\";'),('date_default_timezone','s:13:\"Europe/Madrid\";'),('default_language','s:3:\"eng\";'),('default_nodes_main','s:1:\"8\";'),('failed_login_limit','i:5;'),('rows_per_page','i:10;'),('site_description','a:0:{}'),('site_frontpage','a:0:{}'),('site_logo','s:8:\"logo.gif\";'),('site_mail','s:24:\"no-reply@your-domain.com\";'),('site_name','s:17:\"My QuickApps Site\";'),('site_online','s:1:\"1\";'),('site_slogan','s:142:\"Vivamus id feugiat ligula. Nulla facilisi. Integer lacus justo, elementum eget consequat a, molestie nec sapien. Quisque tincidunt, nunc vitae\";'),('site_theme','s:7:\"Default\";'),('user_default_avatar','s:25:\"/img/anonymous_avatar.jpg\";'),('user_mail_activation_body','s:246:\"[user_name],\r\n\r\nYour account at [site_name] has been activated.\r\n\r\nYou may now log in by clicking this link or copying and pasting it into your browser:\r\n\r\n[site_login_url]\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_activation_notify','s:1:\"1\";'),('user_mail_activation_subject','s:57:\"Account details for [user_name] at [site_name] (approved)\";'),('user_mail_blocked_body','s:85:\"[user_name],\r\n\r\nYour account on [site_name] has been blocked.\r\n\r\n-- [site_name] team\";'),('user_mail_blocked_notify','s:1:\"1\";'),('user_mail_blocked_subject','s:56:\"Account details for [user_name] at [site_name] (blocked)\";'),('user_mail_canceled_body','s:86:\"[user_name],\r\n\r\nYour account on [site_name] has been canceled.\r\n\r\n-- [site_name] team\";'),('user_mail_canceled_notify','s:1:\"1\";'),('user_mail_canceled_subject','s:57:\"Account details for [user_name] at [site_name] (canceled)\";'),('user_mail_password_recovery_body','s:273:\"[user_name],\r\n\r\nA request to reset the password for your account has been made at [site_name].\r\nYou may now log in by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nAfter log in you can reset your password.\r\n\r\n-- [site_name] team\";'),('user_mail_password_recovery_subject','s:60:\"Replacement login information for [user_name] at [site_name]\";'),('user_mail_welcome_body','s:301:\"[user_name],\r\n\r\nThank you for registering at [site_name]. You may now activate your account by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nThis link can only be used once to log in.\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_welcome_subject','s:46:\"Account details for [user_name] at [site_name]\";'); +/*!40000 ALTER TABLE `#__variables` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-28 14:36:51 diff --git a/app/Config/Schema/tables/vocabularies.sql b/app/Config/Schema/tables/vocabularies.sql new file mode 100644 index 00000000..dc9db6bc --- /dev/null +++ b/app/Config/Schema/tables/vocabularies.sql @@ -0,0 +1,57 @@ +-- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) +-- +-- Host: localhost Database: quickapps +-- ------------------------------------------------------ +-- Server version 5.5.8 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `#__vocabularies` +-- + +DROP TABLE IF EXISTS `#__vocabularies`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `#__vocabularies` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `required` tinyint(1) NOT NULL DEFAULT '0', + `ordering` int(11) DEFAULT '0', + `modified` int(11) NOT NULL, + `created` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `slug` (`slug`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `#__vocabularies` +-- + +LOCK TABLES `#__vocabularies` WRITE; +/*!40000 ALTER TABLE `#__vocabularies` DISABLE KEYS */; +/*!40000 ALTER TABLE `#__vocabularies` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php new file mode 100644 index 00000000..cb9ffa08 --- /dev/null +++ b/app/Config/bootstrap.php @@ -0,0 +1,193 @@ + 'File')); + +/** + * The settings below can be used to set additional paths to models, views and controllers. + * + * App::build(array( + * 'plugins' => array('/full/path/to/plugins/', '/next/full/path/to/plugins/'), + * 'models' => array('/full/path/to/models/', '/next/full/path/to/models/'), + * 'views' => array('/full/path/to/views/', '/next/full/path/to/views/'), + * 'controllers' => array('/full/path/to/controllers/', '/next/full/path/to/controllers/'), + * 'datasources' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'), + * 'behaviors' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'), + * 'components' => array('/full/path/to/components/', '/next/full/path/to/components/'), + * 'helpers' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'), + * 'vendors' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'), + * 'shells' => array('/full/path/to/shells/', '/next/full/path/to/shells/'), + * 'locales' => array('/full/path/to/locale/', '/next/full/path/to/locale/') + * )); + * + */ + +/** + * As of 1.3, additional rules for the inflector are added below + * + * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); + * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); + * + */ + + App::uses('Spyc', 'vendors'); + App::uses('Folder', 'Utility'); + $__searchPath = array(); + +/** + * Load themes as plugin + */ + $folder = new Folder; + $folder->path = APP . 'View' . DS . 'Themed' . DS; + + $__themes = $folder->read(); + $__themes = $__themes[0]; + + foreach ($__themes as $__tname) { + $__searchPath[] = APP . 'View' . DS . 'Themed' . DS . $__tname . DS . 'Plugin' . DS; + } + + $__searchPath[] = ROOT . DS . 'Modules' . DS; + + App::build(array('plugins' => $__searchPath)); + + $plugins = App::objects('plugins', null, false); + + foreach ($plugins as $plugin) { + CakePlugin::load($plugin, array('bootstrap' => true, 'routes' => true)); + + $ppath = CakePlugin::path($plugin); + $ppath = str_replace(DS . $plugin . DS, DS . Inflector::underscore($plugin) . DS, $ppath); + + if (file_exists($ppath . 'Fields' . DS)) { + App::build(array('plugins' => array($ppath . 'Fields' . DS))); + } + } + + $plugins = App::objects('plugins', null, false); + + foreach($plugins as $plugin) { + if (!CakePlugin::loaded($plugin)) { + CakePlugin::load($plugin, array('bootstrap' => true, 'routes' => true) ); + } + } + + unset($__searchPath, $__themes, $__tname, $folder, $plugins, $plugin); + +/** + * Return only the methods for the object you indicate. It will strip out the inherited methods + * web: http://php.net/manual/es/function.get-class-methods.php + * author: onesimus at cox dot net + * date: 19-Jun-2004 09:32 + */ + function get_this_class_methods($class){ + $array1 = get_class_methods($class); + + if ($parent_class = get_parent_class($class)) { + $array2 = get_class_methods($parent_class); + $array3 = array_diff($array1, $array2); + } else { + $array3 = $array1; + } + + return($array3); + } + +/** + * Translation function, domain search order: current plugin, default (global) + * + * @param string $singular String to translate + * @return string the translated string + */ + function __t($singular, $args = null) { + if (!$singular) { + return; + } + + App::uses('I18n', 'I18n'); + $route = Router::getParams(); + $translated = I18n::translate($singular, null, $route['plugin']); # look in plugin + + if ($translated === $singular) { # look in default + $translated = I18n::translate($singular, null, 'default'); + } + + if ($translated === $singular) { # llok in transtalion db-cache + $cache = Cache::read(md5($singular) . '_' . Configure::read('Config.language'), 'i18n'); + $translated = $cache ? $cache: $singular; + } + + if ($args === null) { + return $translated; + } elseif (!is_array($args)) { + $args = array_slice(func_get_args(), 1); + } + + return vsprintf($translated, $args); + } + +/** + * Create Unique Arrays using an md5 hash + * + * @param array $array + * @return array + */ + function arrayUnique($array, $preserveKeys = false) { + $arrayRewrite = array(); + $arrayHashes = array(); + + foreach ($array as $key => $item) { + $hash = md5(serialize($item)); + + if (!isset($arrayHashes[$hash])) { + $arrayHashes[$hash] = $hash; + + if ($preserveKeys) { + $arrayRewrite[$key] = $item; + } else { + $arrayRewrite[] = $item; + } + } + } + + return $arrayRewrite; + } + +/** + * replace the first ocurrence only + * + * @param string $str_pattern what to find for + * @param string $str_replacement the replacement for $str_pattern + * @param string $string the original to find and replace + * @return string + */ + function str_replace_once($str_pattern, $str_replacement, $string) { + if (strpos($string, $str_pattern) !== false) { + $occurrence = strpos($string, $str_pattern); + return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); + } + return $string; + } \ No newline at end of file diff --git a/app/Config/core.php b/app/Config/core.php new file mode 100644 index 00000000..7dcb9e62 --- /dev/null +++ b/app/Config/core.php @@ -0,0 +1,327 @@ + 0 + * and log errors with CakeLog when debug = 0. + * + * Options: + * + * - `handler` - callback - The callback to handle errors. You can set this to any callback type, + * including anonymous functions. + * - `level` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Include stack traces for errors in log files. + * + * @see ErrorHandler for more information on error handling and configuration. + */ + Configure::write('Error', array( + 'handler' => 'ErrorHandler::handleError', + 'level' => E_ALL & ~E_DEPRECATED, + 'trace' => true + )); + +/** + * Configure the Exception handler used for uncaught exceptions. By default, + * ErrorHandler::handleException() is used. It will display a HTML page for the exception, and + * while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0, + * framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type, + * including anonymous functions. + * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you + * should place the file for that class in app/Error. This class needs to implement a render method. + * - `log` - boolean - Should Exceptions be logged? + * + * @see ErrorHandler for more information on exception handling and configuration. + */ + Configure::write('Exception', array( + 'handler' => 'ErrorHandler::handleException', + 'renderer' => 'ExceptionRenderer', + 'log' => true + )); + +/** + * Application wide charset encoding + */ + Configure::write('App.encoding', 'UTF-8'); + +/** + * To configure CakePHP *not* to use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * + * /.htaccess + * /app/.htaccess + * /app/webroot/.htaccess + * + * And uncomment the App.baseUrl below: + */ + //Configure::write('App.baseUrl', env('SCRIPT_NAME')); + +/** + * Uncomment the define below to use CakePHP prefix routes. + * + * The value of the define determines the names of the routes + * and their associated controller actions: + * + * Set to an array of prefixes you want to use in your application. Use for + * admin or other prefixed routes. + * + * Routing.prefixes = array('admin', 'manager'); + * + * Enables: + * `admin_index()` and `/admin/controller/index` + * `manager_index()` and `/manager/controller/index` + * + */ + Configure::write('Routing.prefixes', array('admin')); + +/** + * Turn off all caching application-wide. + * + */ + //Configure::write('Cache.disable', true); + +/** + * Enable cache checking. + * + * If set to true, for view caching you must still use the controller + * public $cacheAction inside your controllers to define caching settings. + * You can either set it controller-wide by setting public $cacheAction = true, + * or in each action using $this->cacheAction = true. + * + */ + //Configure::write('Cache.check', true); + +/** + * Defines the default error type when using the log() function. Used for + * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG. + */ + define('LOG_ERROR', 2); + +/** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The defaults key is + * used to define a default preset to use for sessions, any settings declared here will override + * the settings of the default config. + * + * ## Options + * + * - `Session.name` - The name of the cookie to use. Defaults to 'CAKEPHP' + * - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP + * - `Session.cookieTimeout` - The number of minutes you want session cookies to live for. + * - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the + * value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX + * - `Session.defaults` - The default configuration set to use as a basis for your session. + * There are four builtins: php, cake, cache, database. + * - `Session.handler` - Can be used to enable a custom session handler. Expects an array of of callables, + * that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler` + * to the ini array. + * - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and + * sessionids that change frequently. See CakeSession::$requestCountdown. + * - `Session.ini` - An associative array of additional ini values to set. + * + * The built in defaults are: + * + * - 'php' -Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at /app/Model/Datasource/Session/.php. + * Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to + * + * To use database sessions, run the app/Config/Schema/sessions.php schema using + * the cake shell command: cake schema create Sessions + * + */ + Configure::write('Session', array( + 'defaults' => 'php' + )); + +/** + * The level of CakePHP security. + */ + Configure::write('Security.level', 'medium'); + +/** + * A random string used in security hashing methods. + */ + Configure::write('Security.salt', 'alknLsnÑQUCMdnweuNsd83P390dm'); + +/** + * A random numeric string (digits only) used to encrypt/decrypt strings. + */ + Configure::write('Security.cipherSeed', '8701467490498213157498310'); + +/** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. This is + * useful for invalidating browser caches. + * + * Set to `true` to apply timestamps, when debug = 0, or set to 'force' to always enable + * timestamping. + */ + //Configure::write('Asset.timestamp', true); +/** + * Compress CSS output by removing comments, whitespace, repeating tags, etc. + * This requires a/var/cache directory to be writable by the web server for caching. + * and /vendors/csspp/csspp.php + * + * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css(). + */ + //Configure::write('Asset.filter.css', 'css.php'); + +/** + * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the + * output, and setting the config below to the name of the script. + * + * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link(). + */ + //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php'); + +/** + * The classname and database used in CakePHP's + * access control lists. + */ + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + +/** + * If you are on PHP 5.3 uncomment this line and correct your server timezone + * to fix the date & time related errors. + */ + //date_default_timezone_set('UTC'); + +/** + * + * Cache Engine Configuration + * Default settings provided below + * + * File storage engine. + * + * Cache::config('default', array( + * 'engine' => 'File', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path + * 'prefix' => 'cake_', //[optional] prefix every cache file with this string + * 'lock' => false, //[optional] use file locking + * 'serialize' => true, [optional] + * )); + * + * + * APC (http://pecl.php.net/package/APC) + * + * Cache::config('default', array( + * 'engine' => 'Apc', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * )); + * + * Xcache (http://xcache.lighttpd.net/) + * + * Cache::config('default', array( + * 'engine' => 'Xcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'user' => 'user', //user from xcache.admin.user settings + * 'password' => 'password', //plaintext password (xcache.admin.pass) + * )); + * + * + * Memcache (http://www.danga.com/memcached/) + * + * Cache::config('default', array( + * 'engine' => 'Memcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'servers' => array( + * '127.0.0.1:11211' // localhost, default port 11211 + * ), //[optional] + * 'persistent' => true, // [optional] set this to false for non-persistent connections + * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) + * 'persistent' => true, // [optional] set this to false for non-persistent connections + * )); + * + */ + +/** + * Pick the caching engine to use. If APC is enabled use it. + * If running via cli - apc is disabled by default. ensure it's available and enabled in this case + * + */ +$engine = 'File'; +if (extension_loaded('apc') && (php_sapi_name() !== 'cli' || ini_get('apc.enable_cli'))) { + $engine = 'Apc'; +} + +// In development mode, caches should expire quickly. +$duration = '+999 days'; +if (Configure::read('debug') >= 1) { + $duration = '+10 seconds'; +} + +/** + * Configure the cache used for general framework caching. Path information, + * object listings, and translation cache files are stored with this configuration. + */ +Cache::config('_cake_core_', array( + 'engine' => $engine, + 'prefix' => 'cake_core_', + 'path' => CACHE . 'persistent' . DS, + 'serialize' => ($engine === 'File'), + 'duration' => $duration +)); + +/** + * Configure the cache for model, and datasource caches. This cache configuration + * is used to store schema descriptions, and table listings in connections. + */ +Cache::config('_cake_model_', array( + 'engine' => $engine, + 'prefix' => 'cake_model_', + 'path' => CACHE . 'models' . DS, + 'serialize' => ($engine === 'File'), + 'duration' => $duration +)); diff --git a/app/Config/database.php.install b/app/Config/database.php.install new file mode 100644 index 00000000..aecfe3cb --- /dev/null +++ b/app/Config/database.php.install @@ -0,0 +1,71 @@ + The name of a supported driver; valid options are as follows: + * Database/Mysql - MySQL 4 & 5, + * Database/Sqlite - SQLite (PHP5 only), + * Database/Postgres - PostgreSQL 7 and higher, + * Database/Sqlserver - Microsoft SQL Server 2005 and higher, + * Database/Oracle - Oracle 8 and higher + * + * You can add custom database drivers (or override existing drivers) by adding the + * appropriate file to app/Model/Datasource/Database. Drivers should be named 'MyDriver.php', + * + * + * persistent => true / false + * Determines whether or not the database should use a persistent connection + * + * host => + * the host you connect to the database. To add a socket or port number, use 'port' => # + * + * prefix => + * Uses the given prefix for all the tables in this database. This setting can be overridden + * on a per-table basis with the Model::$tablePrefix property. + * + * schema => + * For Postgres specifies which schema you would like to use the tables in. Postgres defaults to 'public'. + * + * encoding => + * For MySQL, Postgres specifies the character encoding to use when connecting to the + * database. Uses database default not specified. + * + */ +class DATABASE_CONFIG { + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => {db_persistent}, + 'host' => '{db_host}', + 'login' => '{db_login}', + 'password' => '{db_password}', + 'database' => '{db_database}', + 'prefix' => '{db_prefix}', + 'encoding' => 'UTF8' + ); +} diff --git a/app/Config/email.php.default b/app/Config/email.php.default new file mode 100644 index 00000000..f55496ae --- /dev/null +++ b/app/Config/email.php.default @@ -0,0 +1,88 @@ + The name of a supported transport; valid options are as follows: + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', + * where 'Your' is the name of the transport. + * + * from => + * The origin email. See CakeEmail::from() about the valid values + * + */ +class EmailConfig { + + public $default = array( + 'transport' => 'Mail', + 'from' => 'you@localhost' + ); + + public $smtp = array( + 'transport' => 'Smtp', + 'from' => array('My Site', 'site@localhost'), + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null + ); + + public $fast = array( + 'from' => 'you@localhost', + 'sender' => null, + 'to' => null, + 'cc' => null, + 'bcc' => null, + 'replyTo' => null, + 'readReceipt' => null, + 'returnPath' => null, + 'messageId' => true, + 'subject' => null, + 'message' => null, + 'headers' => null, + 'viewRender' => null, + 'template' => false, + 'layout' => false, + 'viewVars' => null, + 'attachments' => null, + 'emailFormat' => null, + 'transport' => 'Smtp', + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null + ); + +} diff --git a/app/Config/routes.php b/app/Config/routes.php new file mode 100644 index 00000000..a4fbb328 --- /dev/null +++ b/app/Config/routes.php @@ -0,0 +1,53 @@ + 'install')); + } else { + Router::connect('/', array('plugin' => 'node', 'controller' => 'node', 'action' => 'index')); + Router::connect('/admin', array('plugin' => 'system', 'controller' => 'system', 'action' => 'index', 'admin' => true )); + } + +/** + * Load all plugin routes. See the CakePlugin documentation on + * how to customize the loading of plugin routes. + */ + CakePlugin::routes(); + +/** + * Load the CakePHP default routes. Remove this if you do not want to use + * the built-in default routes. + */ + require CAKE . 'Config' . DS . 'routes.php'; \ No newline at end of file diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php new file mode 100644 index 00000000..39d1d25a --- /dev/null +++ b/app/Controller/AppController.php @@ -0,0 +1,294 @@ + + * @link http://cms.quickapps.es + */ +class AppController extends Controller { + public $view = 'Theme'; + public $theme = 'default'; + + public $Layout = array( + 'feed' => null, # url to rss feed + 'blocks' => array(), + 'node' => array(), + 'viewMode' => '', # full, list + 'header' => array(), # extra code for header + 'footer' => array(), # extra code for + 'stylesheets' => array( + 'all' => array(), + 'braille' => array(), + 'embossed' => array(), + 'handheld' => array(), + 'print' => array(), + 'projection' => array(), + 'screen' => array(), + 'speech' => array(), + 'tty' => array(), + 'tv' => array(), + 'embed' => array() + ), + 'javascripts' => array( + 'embed' => array(), + 'file' => array('jquery.js', 'quickapps.js') + ), + 'meta' => array() # meta tags for layout + ); + + public $helpers = array( + 'Layout', + 'Form' => array('className' => 'QaForm'), + 'Html' => array('className' => 'QaHtml'), + 'Session', + 'Cache', + 'Js', + 'Time' + ); + + public $uses = array( + 'System.Variable', + 'System.Module', + 'Menu.MenuLink', + 'Locale.Language' + ); + + public $components = array( + 'Session', + 'Cookie', + 'RequestHandler', + 'Hook', + 'Acl', + 'Auth', + 'Quickapps' + ); + + public function __construct($request = null, $response = null) { + $this->__preloadHooks(); + parent::__construct($request, $response); + } + + public function beforeRender() { + if ($this->Layout['feed']) { + $this->Layout['meta']['link'] = $this->Layout['feed']; + } + + $this->set('Layout', $this->Layout); + + if ($this->name == 'CakeError') { + $this->beforeFilter(); + $this->layout = 'error'; + } + + return true; + } + + public function isAuthorized($user) { + $this->Quickapps->accessCheck(); + + $isAllowed = ( + $this->Auth->allowedActions == array('*') || + in_array($this->request->params['action'], $this->Auth->allowedActions) + ); + + return $isAllowed; + } + +/** + * shortcut for $this->set(`title_for_layout`...) + * + * @param string $str layout title + * @return void + */ + public function title($str) { + return $this->Quickapps->title($str); + } + +/** + * shortcut for Session setFlash + * + * @param string $msg mesagge to display + * @param string $class type of message: error, success, alert, bubble + * @return void + */ + public function flashMsg($msg, $class = 'success') { + return $this->Quickapps->flashMsg($msg, $class); + } + +/** + * Insert custom block in stack + * + * @param array $data formatted block array + * @param string $region theme region where to push + * @return boolean + */ + public function blockPush($block = array(), $region = null, $show_on = true) { + return $this->Quickapps->blockPush($block, $region, $show_on); + } + +/** + * Wrapper method to Hook::hook_defined + * + * @param string $hook Name of the event + * @return bool + */ + public function hook_defined($hook) { + return $this->Hook->hook_defined($hook); + } + +/** + * Overwrite default options for Hook dispatcher. + * Useful when calling a hook with non-parameter and custom options. + * + * Watch out!: Hook dispatcher automatic reset its default options to + * the original ones after `hook()` is invoked. + * Means that if you need to call more than one hook (consecutive) with no parameters and + * same options ** you must call `setHookOptions()` after each hook() ** + * + * ### Usage + * For example in any controller action: + * {{{ + * $this->setHookOptions(array('collectReturn' => false)); + * $response = $this->hook('collect_hook_with_no_parameters'); + * + * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); + * $response2 = $this->hook('no_collect_and_breakOn_hook_with_no_parameters'); + * }}} + * + * @param array $options Array of options to overwrite + * @return void + */ + public function setHookOptions($options) { + $this->Hook->setHookOptions($options); + } + +/** + * Wrapper method to Hook::__dispatchEvent + * + * @param string $hook Name of the event + * @param mix $data Any data to attach + * @param bool $raw_return false means return asociative data, true will return a listed array + * @return mixed FALSE -or- result array + */ + public function hook($hook, &$data = array(), $options = array()) { + return $this->Hook->hook($hook, $data, $options); + } + +/** + * Set crumb from url parse or add url to the links list + * + * @param mixed $url if is array then will push de formated array to the crumbs list + * else will set base crum from string parsing + * @return void + */ + public function setCrumb($url = false) { + return $this->Quickapps->setCrumb($url); + } + +/** + * Load and attach hooks to AppController. (Hooks can be Components, Helpers, Behaviours) + * - Preload helpers hooks + * - Preload behaviors hooks + * - Preload components hooks + * + * @return void + */ + private function __preloadHooks() { + $paths = $c = $h = $b = array(); + + // load current theme hooks only + $_variable = Cache::read('Variable'); + $_modules = Cache::read('Modules'); + $_themeType = Router::getParam('admin') ? 'admin_theme' : 'site_theme'; + + if (!$_variable) { + $this->loadModel('System.Variable'); + + $_variable = $this->Variable->find('first', array('conditions' => array('Variable.name' => $_themeType))); + $_variable[$_themeType] = $_variable['Variable']['value']; + + ClassRegistry::flush(); + unset($this->Variable); + } + + if (!$_modules) { + $this->loadModel('System.Module'); + + foreach ($this->Module->find('all', array('fields' => array('name'), 'conditions' => array('Module.status' => 1))) as $m) { + $_modules[$m['Module']['name']] = array(); + } + + ClassRegistry::flush(); + unset($this->Module); + } + + $_modules = array_keys($_modules); + $themeToUse = $_variable[$_themeType]; + $plugins = App::objects('plugin', null, false); + + foreach ($plugins as $plugin) { + $ppath = CakePlugin::path($plugin); + $modulesCache = Cache::read('Modules'); + $_plugin = Inflector::underscore($plugin); + + # inactive module, except fields that are nor registered as plugin en DB + if (!in_array($_plugin, $_modules) && strpos($ppath, DS . 'Fields' . DS) === false) { + continue; + } + + if ((isset($modulesCache[$_plugin]['status']) && $modulesCache[$_plugin]['status'] == 0) || + (strpos($ppath, DS . 'View' . DS . 'Themed') !== false && strpos($ppath, 'Themed' . DS . $themeToUse . DS . 'Plugin') === false) + ) { + continue; # Important: skip no active themes + } + + $paths["{$plugin}_components"] = $ppath . 'Controller' . DS . 'Component' . DS; + $paths["{$plugin}_behaviors"] = $ppath . 'Model' . DS . 'Behavior' . DS; + $paths["{$plugin}_helpers"] = $ppath . 'View' . DS . 'Helper' . DS; + } + + $paths = array_merge( + array( + APP . 'Controller' . DS . 'Components' . DS, # core components + APP . 'View' . DS . 'Helper' . DS, # core helpers + APP . 'Model' . DS . 'Behavior' . DS # core behaviors + ), + (array)$paths + ); + + $folder = new Folder; + + foreach ($paths as $key => $path) { + $folder->path = $path; + $files = $folder->find('(.*)Hook(Component|Behavior|Helper)\.php'); + $plugin = is_string($key) ? explode('_', $key) : false; + $plugin = is_array($plugin) ? $plugin[0] : $plugin; + + foreach ($files as $file) { + $prefix = ($plugin) ? Inflector::camelize($plugin) . '.' : ''; + $hook = $prefix . Inflector::camelize(str_replace(array('.php'), '', basename($file))); + $hook = str_replace(array('Component', 'Behavior', 'Helper'),'', $hook); + + if (strpos($path, 'Helper')) { + $h[] = $hook; + $this->helpers[] = $hook; + } elseif (strpos($path, 'Behavior')) { + $b[] = $hook; + } else { + $c[] = $hook; + $this->components[] = $hook; + } + } + } + + $h[] = 'CustomHooks'; # merge custom hooktags helper + + Configure::write('Hook.components', $c); + Configure::write('Hook.behaviors', $b); + Configure::write('Hook.helpers', $h); + } +} \ No newline at end of file diff --git a/app/Controller/Component/HookComponent.php b/app/Controller/Component/HookComponent.php new file mode 100644 index 00000000..4f1aa65e --- /dev/null +++ b/app/Controller/Component/HookComponent.php @@ -0,0 +1,332 @@ + + * @link http://cms.quickapps.es + */ +class HookComponent extends Component { + public $Controller; + public $listeners = array(); + public $events = array(); + public $eventMap = array(); + public $Options = array( + 'break' => false, + 'breakOn' => false, + 'collectReturn' => false + ); + private $__Options = array( + 'break' => false, + 'breakOn' => false, + 'collectReturn' => false + ); + + public function startup() { } + public function beforeRender() { } + public function shutdown() { } + public function beforeRedirect() {} + +/** + * Called before the Controller::beforeFilter(). + * + * @param object $controller Controller with components to initialize + * @return void + */ + public function initialize(&$Controller) { + $this->Controller =& $Controller; + $eventMap = array(); + + foreach (Configure::read('Hook.components') as $component) { + $component = strpos($component, '.') !== false ? substr($component, strpos($component, '.')+1) : $component; + + if (strpos($component, 'Hook')) { + $methods = array(); + $_methods = get_this_class_methods($this->Controller->{$component}); + + foreach ($_methods as $method) { + $methods[] = $method; + $eventMap[$method] = (string)$component; + } + + $this->listeners[$component] = $methods; + $this->events = array_merge($this->events, $methods); + $this->eventMap = array_merge($this->eventMap, $eventMap); + } + } + + $this->events = array_unique($this->events); + + return true; + } + +/** + * Parse string for special placeholders + * placeholder example: [hook_function param1=text param=2 param3=0 ... /] + * [other_hook_function]only content & no params[/other_hook_function] + * + * @return string HTML + */ + public function hookTags($text) { + $text = $this->specialTags($text); + $tags = implode('|', $this->events); + + return preg_replace_callback('/(.?)\[(' . $tags . ')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s', array($this, '__doHookTag'), $text); + } + +/** + * Replace some core useful tags: + * `[date=FORMAT]` Return current date(FORMAT). + * `[language.OPTION]` Current language option (code, name, native, direction). + * `[language]` Shortcut to [language.code] which return current language code. + * `[url]YourURL[/url]` or `[url=YourURL]` Formatted url. + * `[url=LINK]LABEL[/url]` Returns link tag LABEL + * `[t=stringToTranslate]` or `[t]stringToTranslate[/t]` text translation: __t(stringToTranslate) + * `[t=domain@@stringToTranslate]` Translation by domain __d(domain, stringToTranslate) + * + * @param string $text original text where to replace tags + * @return string + */ + public function specialTags($text) { + // [locale] + $text = str_replace('[language]', Configure::read('Variable.language.code'), $text); + + //[locale.OPTION] + preg_match_all('/\[language.(.+)\]/iUs', $text, $localeMatches); + foreach ($localeMatches[1] as $attr) { + $text = str_replace("[language.{$attr}]", Configure::read('Variable.language.' .$attr), $text); + } + + //[url]URL[/url] + preg_match_all('/\[url\](.+)\[\/url\]/iUs', $text, $urlMatches); + foreach ($urlMatches[1] as $url) { + $text = str_replace("[url]{$url}[/url]", $this->_View->Html->url($url, true), $text); + } + + //[url=URL] + preg_match_all('/\[url\=(.+)\]/iUs', $text, $urlMatches); + foreach ($urlMatches[1] as $url) { + $text = str_replace("[url={$url}]", $this->_View->Html->url($url, true), $text ); + } + + //[t=text to translate] + preg_match_all('/\[t\=(.+)\]/iUs', $text, $tMatches); + foreach ($tMatches[1] as $string) { + $text = str_replace("[t={$string}]", __t($string), $text); + } + + //[t]text to translate[/t] + preg_match_all('/\[t\](.+)\[\/t\]/iUs', $text, $tMatches); + foreach ($tMatches[1] as $string) { + $text = str_replace("[t]{$string}[/t]", __t($string), $text); + } + + //[t=domain@@text to translate] + preg_match_all('/\[t\=(.+)\@\@(.+)\]/iUs', $text, $dMatches); + foreach ($dMatches[1] as $key => $domain) { + $text = str_replace("[d={$domain}@@{$dMatches[2][$key]}]", __d($domain, $dMatches[2][$key]), $text ); + } + + //[date=FORMAT@@TIME_STAMP] + preg_match_all('/\[date\=(.+)\@\@(.+)\]/iUs', $text, $dateMatches); + foreach ($dateMatches[1] as $key => $format) { + $stamp = $dateMatches[2][$key]; + $replace = is_numeric($stamp) ? date($format, $stamp) : date($format, strtotime($stamp)); + $text = str_replace("[date={$format}@@{$stamp}]", $replace, $text); + } + + //[date=FORMAT] + preg_match_all('/\[date\=(.+)\]/iUs', $text, $dateMatches); + foreach ($dateMatches[1] as $format) { + $text = str_replace("[date={$format}]", date($format), $text); + } + + # pass text to modules so they can apply their own special tags + $this->hook('specialTags_alter', $text); + + return $text; + } + +/** + * Chech if hook exists + * + * @param string $hook Name of the hook to check + * @return boolean + */ + public function hook_defined($hook) { + return (in_array($hook, $this->events) == true); + } + +/** + * Trigger a callback method on every HookComponent. + * + * ### Options + * + * - `breakOn` Set to the value or values you want the callback propagation to stop on. + * Can either be a scalar value, or an array of values to break on. + * Defaults to `false`. + * + * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value + * will be returned. If used in combination with `collectReturn` the collected results will be returned. + * Defaults to `false`. + * + * - `collectReturn` Set to true to collect the return of each object into an array. + * This array of return values will be returned from the hook() call. Defaults to `false`. + * + * - `alter` Allows each callback gets called on to modify the parameters to the next object. + * Defaults to true. + * + * @param string $event name of the hook to call + * @param mixed $data data for the triggered callback + * @param array $option Array of options + * @return mixed Either the last result or all results if collectReturn is on. Or null in case of no response + */ + public function hook($event, &$data = array(), $options = array()) { + return $this->__dispatchEvent($event, $data, $options); + } + +/** + * Overwrite default options for Hook dispatcher. + * Useful when calling a hook with non-parameter and custom options. + * + * Watch out!: Hook dispatcher automatic reset its default options to + * the original ones after `hook()` is invoked. + * Means that if you need to call more than one hook (consecutive) with no parameters and + * same options ** you must call `setHookOptions()` after each hook() ** + * + * ### Usage + * For example in any controller action: + * {{{ + * $this->setHookOptions(array('collectReturn' => false)); + * $response = $this->hook('collect_hook_with_no_parameters'); + * + * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); + * $response2 = $this->hook('OTHER_collect_hook_with_no_parameters'); + * }}} + * + * @param array $options Array of options to overwrite + * @return void + */ + public function setHookOptions($options) { + $this->Options = Set::merge($this->Options, $options); + } + +/** + * Parse hook tags attributes + * + * @param string $text Tag string to parse + * @return Array array of attributes + */ + private function __hookTagParseAtts($text) { + $atts = array(); + $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; + $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); + + if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) { + foreach ($match as $m) { + if (!empty($m[1])) { + $atts[strtolower($m[1])] = stripcslashes($m[2]); + } elseif (!empty($m[3])) { + $atts[strtolower($m[3])] = stripcslashes($m[4]); + } elseif (!empty($m[5])) { + $atts[strtolower($m[5])] = stripcslashes($m[6]); + } elseif (isset($m[7]) and strlen($m[7])) { + $atts[] = stripcslashes($m[7]); + } elseif (isset($m[8])) { + $atts[] = stripcslashes($m[8]); + } + } + } else { + $atts = ltrim($text); + } + + return $atts; + } + +/** + * Callback function + * + * @see hookTags() + * @return mixed Hook response or false in case of no response. + */ + private function __doHookTag($m) { + // allow [[foo]] syntax for escaping a tag + if ($m[1] == '[' && $m[6] == ']') { + return substr($m[0], 1, -1); + } + + $tag = $m[2]; + $attr = $this->__hookTagParseAtts( $m[3] ); + $hook = isset($this->eventMap[$tag]) ? $this->eventMap[$tag] : false; + + if ($hook) { + $hook =& $this->Controller->{$hook}; + + if (isset( $m[5] )) { + // enclosing tag - extra parameter + return $m[1] . call_user_func(array($hook, $tag), $attr, $m[5], $tag) . $m[6]; + } else { + // self-closing tag + return $m[1] . call_user_func(array($hook, $tag), $attr, null, $tag) . $m[6]; + } + } + + return false; + } + +/** + * Dispatch Component-hooks from all the plugins and core + * + * @see HookComponent::hook() + * @return mixed Either the last result or all results if collectReturn is on. Or NULL in case of no response + */ + private function __dispatchEvent($event, &$data = array(), $options = array()) { + $options = array_merge($this->Options, (array)$options); + $collected = array(); + + if (!$this->hook_defined($event)) { + $this->__resetOptions(); + + return null; + } + + foreach ($this->listeners as $component => $methods) { + foreach ($methods as $method) { + if ($method == $event && is_callable(array($this->Controller->{$component}, $method))) { + $result = call_user_func(array($this->Controller->{$component}, $event), $data); + + if ($options['collectReturn'] === true) { + $collected[] = $result; + } + + if ($options['break'] && + ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true))) + ) { + $this->__resetOptions(); + + return $result; + } + } + } + } + + if (empty($collected) && in_array($result, array('', null), true)) { + $this->__resetOptions(); + + return null; + } + + $this->__resetOptions(); + + return $options['collectReturn'] ? $collected : $result; + } + + private function __resetOptions() { + if ($this->Options !== $this->__Options) { + $this->Options = $this->__Options; + } + } +} \ No newline at end of file diff --git a/app/Controller/Component/InstallerComponent.php b/app/Controller/Component/InstallerComponent.php new file mode 100644 index 00000000..42b15128 --- /dev/null +++ b/app/Controller/Component/InstallerComponent.php @@ -0,0 +1,881 @@ + + * @link http://cms.quickapps.es + */ +class InstallerComponent extends Component { + public $errors = array(); + public $Controller; + public $options = array( + 'name' => null, # used while deleting + 'type' => 'module', # type of package to install + 'status' => 1 # install and activate(status=1), install and do not activate (status=0) + ); + + public function startup() {} + public function beforeRender() {} + public function shutdown() {} + public function beforeRedirect() {} + + public function initialize(&$Controller) { + $this->Controller =& $Controller; + + return true; + } + +/** + * Begin instalation proccess for the indicated package. + * Expected module package estructure: + * - module_folder_name/ # $package_path + * - Config/ + * - Controller/ + * - Component/ + * - InstallComponent.php + * - Lib/ + * - Locale/ + * - Model/ + * - View/ + * - webroot/ + * - module_folder_name.yaml + * + * Expected theme package estructure: + * - CamelCaseThemeName/ # $package_path + * - Layouts/ + * - Plugin/ + * - theme_camel_case_theme_name/ # prefix 'theme_'and underscore 'CamelCaseThemeName' (theme name) + * - webroot/ + * - CamelCaseThemeName.yaml + * - thumbnail.png # 206x150px + * + * + * @param array $data form POST submit of the .app package ($this->data) + * @param array $options optional settings, see InstallerComponent::$options + * @return bool true on success or false otherwise + */ + public function install($data = false, $options = array()) { + if (!$data) { + return false; + } + + $oldMask = umask(0); + $this->options = array_merge($this->options, $options); + $ext = strtolower(strrchr($data['Package']['data']['name'], '.')); + + if ($ext !== '.app') { + $this->errors[] = __d('system', 'Invalid package extension. Got `%s`, `.app` expected', $ext); + return false; + } + + /**********/ + /* upload */ + /**********/ + App::import('Vendor', 'Upload'); + + $uploadPath = CACHE. 'installer'; + $workingDir = CACHE . 'installer' . DS . $data['Package']['data']['name'] . DS; + $Folder = new Folder; + $Upload = new Upload($data['Package']['data']); + $Upload->allowed = array('application/*'); + $Upload->file_overwrite = true; + $Upload->file_src_name_ext = 'zip'; + + $Folder->delete($workingDir); + $Upload->Process($workingDir . 'package' . DS); + + if (!$Upload->processed) { + $this->errors[] = __d('system', 'Package upload error') . "

{$Upload->error}

"; + return false; + } + + /*******************/ + /* unzip & install */ + /*******************/ + App::import('Vendor', 'PclZip'); + + $PclZip = new PclZip($Upload->file_dst_pathname); + + if (($v_result_list = $PclZip->extract(PCLZIP_OPT_PATH, $workingDir . 'unzip')) == 0 ) { + $this->errors[] = __d('system', 'Unzip error.') . "

" . $PclZip->errorInfo(true) . "

"; + + return false; + } else { + /* Package Validation */ + $Folder->path = $workingDir . 'unzip' . DS; + $folders = $Folder->read();$folders = $folders[0]; + $packagePath = isset($folders[0]) && count($folders) === 1 ? CACHE . 'installer' . DS . $data['Package']['data']['name'] . DS . 'unzip' . DS . str_replace(DS, '', $folders[0]) . DS : false; + $appName = (string)basename($packagePath); + + if (!$packagePath) { + $this->errors[] = __d('system', 'Invalid package structure after unzip'); + + return false; + } + + switch ($this->options['type']) { + case 'module': + default: + $tests = array( + 'notAlreadyInstalled' => array( + 'test' => ( + $this->Controller->Module->find('count', array('conditions' => array('Module.name' => $appName, 'Module.type' => 'module'))) === 0 && + !file_exists(ROOT . DS . 'Modules' . DS . $appName) + ), + 'header' => __d('system', 'Already Installed'), + 'msg' => __d('system', 'This module is already installed') + ), + 'protectedPrefix' => array( + 'test' => (strpos(Inflector::underscore($appName), 'theme_') === false), + 'header' => __d('system', 'Invalid prefix'), + 'msg' => __d('system', "The prefix 'theme_' is not allowed for modules.") + ), + 'Config' => array( + 'test' => file_exists($packagePath . 'Config'), + 'header' => __d('system', 'Config Folder'), + 'msg' => __d('system', 'Config folder not found') + ), + 'Controller' => array( + 'test' => file_exists($packagePath . 'Controller'), + 'header' => __d('system', 'Controller Folder'), + 'msg' => __d('system', 'Controller folder not found') + ), + 'Component' => array( + 'test' => file_exists($packagePath . 'Controller' . DS . 'Component'), + 'header' => __d('system', 'Component Folder'), + 'msg' => __d('system', 'Component folder not found') + ), + 'InstallComponent.php' => array( + 'test' => file_exists($packagePath . 'Controller' . DS . 'Component' . DS . 'InstallComponent.php'), + 'header' => __d('system', 'Installer File'), + 'msg' => __d('system', 'Installer file (InstallComponent.php) not found') + ), + 'Lib' => array( + 'test' => file_exists($packagePath . 'Lib'), + 'header' => __d('system', 'Lib Folder'), + 'msg' => __d('system', 'Lib folder not found') + ), + 'Locale' => array( + 'test' => file_exists($packagePath . 'Locale'), + 'header' => __d('system', 'Locale Folder'), + 'msg' => __d('system', 'Locale folder not found') + ), + 'Model' => array( + 'test' => file_exists($packagePath . 'Model'), + 'header' => __d('system', 'Model Folder'), + 'msg' => __d('system', 'Model folder not found') + ), + 'yaml' => array( + 'test' => file_exists($packagePath . "{$appName}.yaml"), + 'header' => __d('system', 'YAML File'), + 'msg' => __d('system', 'YAML File (%s) not found', "{$appName}.yaml") + ) + ); + break; + + case 'theme': + $tests = array( + 'notAlreadyInstalled' => array( + 'test' => ( + $this->Controller->Module->find('count', array('conditions' => array('Module.name' => 'theme_' . Inflector::underscore($appName), 'Module.type' => 'theme'))) === 0 && + !file_exists(APP . 'View' . DS . 'Themed' . DS . $appName) + ), + 'header' => __d('system', 'Already Installed'), + 'msg' => __d('system', 'This theme is already installed') + ), + 'CamelCaseName' => array( + 'test' => (Inflector::camelize($appName) == $appName), + 'header' => __d('system', 'Theme name'), + 'msg' => __d('system', 'Invalid theme name (got "%s", expected: "%s")', $appName, Inflector::camelize($appName)) + ), + 'Layouts' => array( + 'test' => file_exists($packagePath . 'Layouts'), + 'header' => __d('system', 'Layouts Folder'), + 'msg' => __d('system', '"Layouts" folder not found') + ), + 'Plugin' => array( + 'test' => file_exists($packagePath . 'Plugin'), + 'header' => __d('system', 'Plugin Folder'), + 'msg' => __d('system', '"Plugin" folder not found') + ), + 'plugin_app' => array( + 'test' => file_exists($packagePath . 'Plugin' . DS . 'theme_' . Inflector::underscore($appName)), + 'header' => __d('system', 'Plugin app'), + 'msg' => __d('system', 'Plugin app ("%s") folder not found', 'theme_' . Inflector::underscore($appName)) + ), + 'InstallComponent.php' => array( + 'test' => file_exists($packagePath . 'Plugin' . DS . 'theme_' . Inflector::underscore($appName). DS . 'Controller' . DS . 'Component' . DS . 'InstallComponent.php'), + 'header' => __d('system', 'Installer File'), + 'msg' => __d('system', 'Installer file (InstallComponent.php) not found') + ), + 'webroot' => array( + 'test' => file_exists($packagePath . 'webroot'), + 'header' => __d('system', 'webroot Folder'), + 'msg' => __d('system', 'webroot folder not found') + ), + 'yaml' => array( + 'test' => file_exists($packagePath . "{$appName}.yaml"), + 'header' => __d('system', 'YAML File'), + 'msg' => __d('system', 'YAML File (%s) not found', "{$appName}.yaml") + ), + 'thumbnail' => array( + 'test' => file_exists($packagePath . 'thumbnail.png'), + 'header' => __d('system', 'Theme thumbnail'), + 'msg' => __d('system', 'Thumbnail image ("%s") not found', 'thumbnail.png') + ) + ); + break; + } + + if (!$this->__process_tests($tests)) { + return false; + } + + /** YAML validations **/ + $yaml = Spyc::YAMLLoad($packagePath . "{$appName}.yaml"); + + switch ($this->options['type']) { + case 'module': + default: + $tests = array( + 'yaml' => array( + 'test' => ( + (isset($yaml['name']) && !empty($yaml['name'])) && + (isset($yaml['description']) && !empty($yaml['description'])) && + (isset($yaml['category']) && !empty($yaml['category'])) && + (isset($yaml['version']) && !empty($yaml['version'])) && + (isset($yaml['core']) && !empty($yaml['core'])) + ), + 'header' => __d('system', 'YAML Validation'), + 'msg' => __d('system', 'Module configuration file (%s) appears to be invalid.', "{$appName}.yaml") + ) + ); + break; + + case 'theme': + $tests = array( + 'yaml' => array( + 'test' => ( + (isset($yaml['info']) && !empty($yaml['info'])) && + (isset($yaml['info']['name']) && !empty($yaml['info']['name'])) && + (isset($yaml['info']['description']) && !empty($yaml['info']['description'])) && + (isset($yaml['info']['version']) && !empty($yaml['info']['version'])) && + (isset($yaml['info']['author']) && !empty($yaml['info']['author'])) && + (isset($yaml['info']['core']) && !empty($yaml['info']['core'])) && + isset($yaml['stylesheets']) && + (isset($yaml['regions']) && !empty($yaml['regions'])) && + (isset($yaml['layout']) && !empty($yaml['layout'])) + ), + 'header' => __d('system', 'YAML Validation'), + 'msg' => __d('system', 'Theme configuration file (%s) appears to be invalid.', "{$appName}.yaml") + ) + ); + break; + } + + if (!$this->__process_tests($tests)) { + $this->errors[] = __d('system', 'Invalid information file (.yaml)'); + + return false; + } + + /** + * validate dependencies and required core version + */ + switch ($this->options['type']) { + case 'module': + $core = "core ({$yaml['core']})"; + $r = $this->checkIncompatibility($this->parseDependency($core), Configure::read('Variable.qa_version')); + + if ($r !== null) { + $this->errors[] = __d('system', 'This module is incompatible with your QuickApps version.'); + + return false; + } + + if (isset($yaml['dependencies']) && $this->checkDependency($yaml)) { + $this->errors[] = __d('system', "This module depends on other modules that you do not have or doesn't meet the version required: %s", implode('
', $yaml['dependencies'])); + + return false; + } + break; + + case 'theme': + $core = "core ({$yaml['info']['core']})"; + $r = $this->checkIncompatibility($this->parseDependency($core), Configure::read('Variable.qa_version')); + + if ($r !== null) { + $this->errors[] = __d('system', 'This theme is incompatible with your QuickApps version.'); + + return false; + } + + if (isset($yaml['info']['dependencies']) && $this->checkDependency($yaml['info'])) { + $this->errors[] = __d('system', "This theme depends on other modules that you do not have or doesn't meet the version required: %s", implode('
', $yaml['info']['dependencies'])); + + return false; + } + break; + } + + /** + * validate custom fields + * Only modules are allowed to define fields. + */ + if ($this->options['type'] == 'module' && file_exists($packagePath . 'Fields')) { + $Folder = new Folder($packagePath . 'Fields'); + $fields = $Folder->read(); + $fieldErrors = false; + + if (isset($fields[0])) { + $fields = $fields[0]; + + foreach ($fields as $field) { + if (file_exists($packagePath . 'Fields' . DS . $field . DS . "{$field}.yaml")) { + $yaml = Spyc::YAMLLoad($packagePath . 'Fields' . DS . $field . DS . "{$field}.yaml"); + + if (!isset($yaml['name']) || !isset($yaml['description'])) { + $fieldErrors = true; + $this->errors[] = __d('system', 'invalid information file (.yaml). Field "%s"', $field); + } + } else { + $fieldErrors = true; + $this->errors[] = __d('system', 'Invalid field "%s". Information file (.yaml) not found.', $field); + } + } + } + + if ($fieldErrors) { + return false; + } + } + ### End of validations ### + + + /*****************/ + /**** INSTALL ****/ + /*****************/ + $installComponentPath = $this->options['type'] == 'theme' ? $packagePath . 'Plugin' . DS . 'theme_' . Inflector::underscore($appName) . DS . 'Controller' . DS . 'Component' . DS : $packagePath . 'Controller' . DS . 'Component' . DS; + $Install = $this->loadInstallComponent($installComponentPath); + $r = true; + + if (method_exists($Install, 'beforeInstall')) { + $r = $Install->beforeInstall($this); + } + + if ($r === false) { + return false; + } + + /** Copy files **/ + $copyTo = ($this->options['type'] == 'module') ? ROOT . DS . 'Modules' . DS . $appName : APP . 'View' . DS . 'Themed' . DS . $appName; + $this->rcopy($packagePath, $copyTo); + + /** DB Logics **/ + $moduleData = array( + 'name' => ($this->options['type'] == 'module' ? $appName : 'theme_' . Inflector::underscore($appName)), + 'type' => ($this->options['type'] == 'module' ? 'module' : 'theme' ), + 'status' => intval($this->options['status']) + ); + + $this->Controller->Module->save($moduleData); # register module + + /** Build ACOS && Register module in core **/ + switch ($this->options['type']) { + case 'module': + $this->buildAcos($appName); + break; + + case 'theme': + $this->buildAcos( + 'theme_' . Inflector::underscore($appName), + APP . 'View'. DS . 'Themed' . DS . Inflector::camelize($appName) . DS . 'Plugin' . DS + ); + + App::build(array('plugins' => array(APP . 'View'. DS . 'Themed' . DS . Inflector::camelize($appName) . DS . 'Plugin' . DS))); + break; + } + + /** Delete unziped package **/ + $Folder->delete($workingDir); + + /** Finish **/ + if (method_exists($Install, 'afterInstall')) { + $Install->afterInstall($this); + } + + $this->afterInstall(); + } + + umask($oldMask); + + return true; + } + +/** + * Uninstall plugin by name + * + * @param string $pluginName name of the plugin to uninstall, it could be a theme plugin + * (ThemeMyThemeName or theme_my_theme_name) or module plugin + * (MyModuleName or my_module_name) + * @return boolean true on success or false otherwise + */ + public function uninstall($pluginName = false) { + if (!$pluginName || !is_string($pluginName)) { + return false; + } + + $this->options['name'] = $pluginName; + $name = Inflector::underscore($this->options['name']); + $Name = Inflector::camelize($this->options['name']); + $pData = $this->Controller->Module->findByName($name); + + if (!$pData) { + return false; + } + + /* useful for before/afterUninstall */ + $this->options['type'] = $pData['Module']['type']; + $this->options['__data'] = $pData; + $this->options['__path'] = $pData['Module']['type'] == 'theme' ? APP . 'View' . DS . 'Themed' . DS . str_replace('Theme', '', $Name) . DS . 'Plugin' . DS . $name . DS : CakePlugin::path($Name); + $this->options['__name'] = $name; + $this->options['__Name'] = $Name; + + # core plugins can not be deleted + if (in_array($pluginName, array_merge(array('ThemeDefault', 'ThemeAdminDefault'), Configure::read('coreModules')))) { + return false; + } + + $pluginPath = $this->options['__path']; + + if (!file_exists($pluginPath)) { + return false; + } + + $Install =& $this->loadInstallComponent($pluginPath . 'Controller' . DS . 'Component' . DS); + + if (!is_object($Install)) { + return false; + } + + $r = true; + + if (method_exists($Install, 'beforeUninstall')) { + $r = $Install->beforeUninstall($this); + } + + if ($r === false) { + return false; + } + + if (!$this->Controller->Module->deleteAll(array('Module.name' => $name))) { + return false; + } + + /** + * Theme Controller does not allow to delete in-use-theme, + * but for precaution we assign to Core Default ones if for some reason + * the in-use-theme is being deleted. + */ + if ($this->options['type'] == 'theme') { + if (Configure::read('Variable.site_theme') == str_replace('Theme', '', $Name)) { + ClassRegistry::init('System.Variable')->save( + array( + 'name' => 'site_theme', + 'value' => 'Default' + ) + ); + } elseif (Configure::read('Variable.admin_theme') == str_replace('Theme', '', $Name)) { + ClassRegistry::init('System.Variable')->save( + array( + 'name' => 'admin_theme', + 'value' => 'DefaultDefault' + ) + ); + } + } + + if (method_exists($Install, 'afterUninstall')) { + $Install->afterUninstall($this); + } + + $this->afterUninstall(); + + return true; + } + + public function enable() { + + } + + public function disable() { + + } + + public function beforeInstall() { + return true; + } + + public function beforeUninstall() { + return true; + } + + public function afterInstall() { + Cache::delete('Modules'); + Cache::delete('Variable'); + + $this->Controller->Quickapps->loadVariables(); + $this->Controller->Quickapps->loadModules(); + + return true; + } + + public function afterUninstall() { + # delete & regenerate caches + Cache::delete('Modules'); + Cache::delete('Variable'); + + $this->Controller->Quickapps->loadModules(); + $this->Controller->Quickapps->loadVariables(); + + # delete all menus created by module/theme + ClassRegistry::init('Menu.Menu')->deleteAll( + array( + 'Menu.module' => $this->options['__name'] + ) + ); + + # delete blocks + ClassRegistry::init('Block.Block')->deleteAll( + array( + 'Block.module' => $this->options['__name'] + ) + ); + + # delete acos branch + $rootAco = $this->Controller->Acl->Aco->find('first', + array( + 'conditions' => array( + 'Aco.alias' => $this->options['__Name'], + 'Aco.parent_id' => null + ) + ) + ); + + $this->Controller->Acl->Aco->delete($rootAco['Aco']['id']); + + # delete node types + ClassRegistry::init('Node.NodeType')->deleteAll( + array( + 'NodeType.module' => $this->options['__name'] + ) + ); + + # delete app folder + $folderpath = ($this->options['type'] == 'module') ? $this->options['__path'] : dirname(dirname($this->options['__path'])); + $Folder = new Folder($folderpath); + $Folder->delete(); + } + +/** + * Creates acos for especified plugin by parsing its Controller folder. + * Plugin's fields are also analyzed. + * Usage example: + * {{{ + * buildAcos('user', APP . 'Plugin' . DS); // Core plugin + * }}} + * + * @param String $plugin underscored plugin name to analyze + * @param mixed $pluginPath Optional (string) plugin full base path. If it is set to false + * then ROOT/Modules is used as default base path. + * @return void + */ + public function buildAcos($plugin, $pluginPath = false) { + $pluginPath = !$pluginPath ? ROOT . DS . 'Modules' . DS : str_replace(DS.DS, DS, $pluginPath . DS); + + if (!file_exists($pluginPath . $plugin)) { + return false; + } + + $__folder = new Folder; + $cPath = $pluginPath . $plugin . DS . 'Controller' . DS; + $__folder->path = $cPath; + $controllers = $__folder->read(); $controllers = $controllers[1]; + + if (count($controllers) === 0) { + return false; + } + + $appControllerPath = $cPath . Inflector::camelize($plugin) . 'AppController.php'; + + if (file_exists($appControllerPath)) { + include_once($appControllerPath); + } + + $this->Controller->Acl->Aco->create(); + $this->Controller->Acl->Aco->save(array('alias' => Inflector::camelize($plugin))); + + $_parent_id = $this->Controller->Acl->Aco->getInsertID(); + + foreach ($controllers as $c) { + if (strpos($c, 'AppController.php') !== false) { + continue; + } + + include_once($cPath . $c); + + $className = str_replace('.php', '', $c); + $methods = get_this_class_methods($className); + + foreach ($methods as $i => $m) { + if (strpos($m, '__') === 0 || + strpos($m, '_') === 0 || + in_array($m, array('beforeFilter', 'beforeRender', 'beforeRedirect', 'afterFilter')) + ) { # ignore private and callback methods + unset($methods[$i]); + } + } + + $this->Controller->Acl->Aco->create(); + $this->Controller->Acl->Aco->save( + array( + 'parent_id' => $_parent_id, + 'alias' => str_replace('Controller', '', $className) + ) + ); + + $parent_id = $this->Controller->Acl->Aco->getInsertID(); + + foreach ($methods as $m) { + $this->Controller->Acl->Aco->create(); + $this->Controller->Acl->Aco->save( + array( + 'parent_id' => $parent_id, + 'alias' => $m + ) + ); + } + } + + # Fields + if (file_exists($pluginPath . $plugin . DS . 'Fields')) { + $__folder->path = $pluginPath . $plugin . DS . 'Fields' . DS; + $fieldsFolders = $__folder->read(); $fieldsFolders = $fieldsFolders[0]; + + foreach ($fieldsFolders as $field) { + $this->buildAcos(basename($field), $pluginPath . $plugin . DS . 'Fields' . DS); + } + } + } + +/** + * By: Drupal + * Parse a dependency for comparison by checkIncompatibility(). + * + * @param $dependency + * A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'. + * @return + * An associative array with three keys: + * - 'name' includes the name of the thing to depend on (e.g. 'foo'). + * - 'original_version' contains the original version string (which can be + * used in the UI for reporting incompatibilities). + * - 'versions' is a list of associative arrays, each containing the keys + * 'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<', + * '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'. + * Callers should pass this structure to checkIncompatibility(). + * + * @see checkIncompatibility() + */ + public function parseDependency($dependency) { + // We use named subpatterns and support every op that version_compare + // supports. Also, op is optional and defaults to equals. + $p_op = '(?P!=|==|=|<|<=|>|>=|<>)?'; + // Core version is always optional: 7.x-2.x and 2.x is treated the same. + $p_core = '(?:' . preg_quote(Configure::read('Variable.qa_version')) . '-)?'; + $p_major = '(?P\d+)'; + // By setting the minor version to x, branches can be matched. + $p_minor = '(?P(?:\d+|x)(?:-[A-Za-z]+\d+)?)'; + $value = array(); + $parts = explode('(', $dependency, 2); + $value['name'] = trim($parts[0]); + + if (isset($parts[1])) { + $value['original_version'] = ' (' . $parts[1]; + + foreach (explode(',', $parts[1]) as $version) { + if (preg_match("/^\s*{$p_op}\s*{$p_core}{$p_major}\.{$p_minor}/", $version, $matches)) { + $op = !empty($matches['operation']) ? $matches['operation'] : '='; + + if ($matches['minor'] == 'x') { + if ($op == '>' || $op == '<=') { + $matches['major']++; + } + + if ($op == '=' || $op == '==') { + $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x'); + $op = '>='; + } + } + + $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']); + } + } + } + + return $value; + } + +/** + * By: Drupal + * Check whether a version is compatible with a given dependency. + * + * @param $v + * The parsed dependency structure from parseDependency(). + * @param $current_version + * The version to check against (like 4.2). + * @return + * NULL if compatible, otherwise the original dependency version string that + * caused the incompatibility. + * + * @see parseDependency() + */ + public function checkIncompatibility($v, $current_version) { + if (!empty($v['versions'])) { + foreach ($v['versions'] as $required_version) { + if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op']))) { + return $v['original_version']; + } + } + } + + return null; + } + +/** + * Verify if all the plugins that $plugin depends on are available and match the required version. + * + * @param string $plugin plugin alias + * @return boolean + */ + public function checkDependency($plugin = null) { + $Plugin = !is_null($plugin) && isset($plugin['yaml']) ? $plugin : Configure::read('Modules.' . Inflector::underscore($plugin)); + + if (isset($Plugin['yaml']['dependencies']) && is_array($Plugin['yaml']['dependencies'])) { + foreach ($Plugin['yaml']['dependencies'] as $p) { + $check = false; + $check = Configure::read('Modules.' . Inflector::underscore($p)); + + if (!$check) { + return false; + } + + $check = $this->checkIncompatibility($this->parseDependency($p), $check['yaml']['version']); + + if (!$check) { + return false; + } + } + } + + return true; + } + +/** + * Verify if there is any plugin that depends of $plugin + * + * @param string $plugin plugin alias + * @param boolean $returnList set to true to return an array list of all plugins that uses $plugin. + * The array list contains all the plugin information Configure::read('Modules.{plugin}') + * @return mixed boolean (if $returnList = false), false return means that there are no plugins that uses $plugin. + * Or an array list of all plugins that uses $plugin ($returnList = true), empty arrray is returned + * if there are no plugins that uses $plugin. + */ + function checkReverseDependency($plugin, $returnList = true) { + $list = array(); + $plugin = Inflector::underscore($plugin); + + foreach (Configure::read('Modules') as $p) { + if (isset($p['yaml']['dependencies']) && + is_array($p['yaml']['dependencies']) + ) { + $dependencies = array(); + + foreach ($p['yaml']['dependencies'] as $d) { + $dependencies[] = $this->parseDependency($d); + } + + $dependencies = Set::extract('{n}.name', $dependencies); + + if (in_array($plugin, $dependencies, true) && $returnList) { + $list[] = $p; + } elseif (in_array($plugin, $dependencies, true)) { + return true; + } + } + } + + if ($returnList) { + return $list; + } + + return false; + } + + private function __process_tests($tests, $header = false) { + $e = 0; + + foreach ($tests as $key => $test) { + if (!$test['test']) { + $e++; + $this->errors[] = $header ? "{$test['header']}

{$test['msg']}

" : "

{$test['msg']}

"; + } + } + + return ($e == 0); + } + + public function loadInstallComponent($search = false) { + if (!file_exists($search . 'InstallComponent.php')) { + return false; + } + + include_once($search . 'InstallComponent.php'); + + $class = "InstallComponent"; + $component = new $class($this->Controller->Components); + + if (method_exists($component, 'initialize')) { + $component->initialize($this); + } + + if (method_exists($component, 'startup')) { + $component->startup($this); + } + + return $component; + } + + public function rcopy($src, $dst) { + $dir = opendir($src); + + @mkdir($dst); + + while(false !== ($file = readdir($dir))) { + if (($file != '.') && ($file != '..')) { + if (is_dir($src . DS . $file)) { + $this->rcopy($src . DS . $file,$dst . DS . $file); + } else { + if (!copy($src . DS . $file, $dst . DS . $file)) { + return false; + } + } + } + } + + closedir($dir); + } +} \ No newline at end of file diff --git a/app/Controller/Component/QuickappsComponent.php b/app/Controller/Component/QuickappsComponent.php new file mode 100644 index 00000000..86f24913 --- /dev/null +++ b/app/Controller/Component/QuickappsComponent.php @@ -0,0 +1,562 @@ + + * @link http://cms.quickapps.es + */ +class QuickAppsComponent extends Component { + public $Controller; + + public function startup() { } + public function beforeRender() { } + public function shutdown() { } + public function beforeRedirect() {} + +/** + * Called before the Controller::beforeFilter(). + * + * @param object $controller Controller with components to initialize + * @return void + */ + public function initialize(&$Controller) { + $this->Controller =& $Controller; + + $this->accessCheck(); + $this->loadVariables(); + $this->loadModules(); + $this->setTheme(); + $this->setTimeZone(); + $this->setLanguage(); + $this->prepareContent(); + $this->siteStatus(); + $this->setCrumb(); + } + + public function siteStatus() { + if (Configure::read('Variable.site_online') != 1 && !$this->isAdmin()) { + if ($this->Controller->plugin != 'user' && + $this->Controller->request->params['controller'] != 'log' && + !in_array($this->Controller->request->params['controller'], array('login', 'logout')) + ) { + # TODO: site down throw + //throw new NotFoundException(__t('Site offline'), 503); + } + } + } + + public function setTheme() { + if (isset($this->Controller->request->params['admin']) && $this->Controller->request->params['admin'] == 1) { + $this->Controller->theme = Configure::read('Variable.admin_theme') ? Configure::read('Variable.admin_theme') : 'admin_default'; + } else { + $this->Controller->theme = Configure::read('Variable.site_theme') ? Configure::read('Variable.site_theme') : 'default'; + } + + $this->Controller->layout ='default'; + $this->Controller->viewClass= 'Theme'; + + if (file_exists(APP . 'View' . DS . 'Themed' . DS . $this->Controller->theme . DS . "{$this->Controller->theme}.yaml")) { + $yaml = Spyc::YAMLLoad(APP . 'View' . DS . 'Themed' . DS . $this->Controller->theme . DS . "{$this->Controller->theme}.yaml"); + $yaml['info']['folder'] = $this->Controller->theme; + $yaml['settings'] = Configure::read('Modules.' . Inflector::underscore("Theme{$this->Controller->theme}") . '.settings'); + + # set custom or default logo + $yaml['settings']['site_logo_url'] = isset($yaml['settings']['site_logo_url']) && !empty($yaml['settings']['site_logo_url']) ? $yaml['settings']['site_logo_url'] : '/img/logo.png'; + + # set custom or default favicon + $yaml['settings']['site_favicon_url'] = isset($yaml['settings']['site_favicon_url']) && !empty($yaml['settings']['site_favicon_url']) ? $yaml['settings']['site_logo_url'] : '/favicon.ico'; + + Configure::write('Theme', $yaml); + + foreach ($yaml['stylesheets'] as $media => $files) { + if (!isset($this->Controller->Layout['stylesheets'][$media])){ + $this->Controller->Layout['stylesheets'][$media] = array(); + } + + foreach ($files as $file) { + $this->Controller->Layout['stylesheets'][$media][] = $file; + } + } + } + + if (Configure::read('Theme.layout')) { + $this->Controller->layout = Configure::read('Theme.layout'); + } + + $this->Controller->hook('stylesheets_alter', $this->Controller->Layout['stylesheets']); # pass css list to modules if they need to alter them (add/remove) + } + + public function prepareContent() { + $theme = Router::getParam('admin') ? Configure::read('Variable.admin_theme') : Configure::read('Variable.site_theme'); + $options = array( + 'conditions' => array( + 'Block.themes_cache LIKE' => "%:{$theme}:%", # only blocks assigned to current theme + 'Block.status' => 1, + 'OR' => array( # only blocks assigned to any/current language + 'Block.locale = ' => null, + 'Block.locale =' => '', + 'Block.locale LIKE ' => '%s:3:"' . Configure::read('Variable.language.code') . '"%', + 'Block.locale' => 'a:0:{}' + ) + ) + ); + + $this->Controller->Layout['blocks'] = $this->Controller->hook('blocks_list', $options, array('collectReturn' => false)); # request blocks to block module + $this->Controller->hook('blocks_alter', $this->Controller->Layout['blocks']); # pass blocks to modules + + /* Basic js files/embed */ + $this->Controller->Layout['javascripts']['embed'][] = ' +jQuery.extend(QuickApps.settings, { + "url": "' . str_replace("//", "/", $this->Controller->here . '/') . '", + "base_url": "' . Router::url('/') . '", + "locale": {"code": "' . Configure::read('Variable.language.code') . '"} +} ); +'; + + $this->Controller->hook('javascripts_alter', $this->Controller->Layout['javascripts']); # pass js to modules + $this->Controller->paginate = array('limit' => Configure::read('Variable.rows_per_page')); + + Configure::write('Variable.qa_version', Configure::read('Modules.system.yaml.version')); + + $defaultMetaDescription = Configure::read('Variable.site_description'); + + if (!empty($defaultMetaDescription)){ + $this->Controller->Layout['meta']['description'] = $defaultMetaDescription; + } + + #auto favicon meta + if (Configure::read('Theme.settings.site_favicon')) { + $faviconURL = Configure::read('Theme.settings.site_favicon_url'); + $this->Controller->Layout['meta']['icon'] = $faviconURL && !empty($faviconURL) ? Router::url($faviconURL) : '/favicon.ico'; + } + } + + public function setLanguage() { + $urlBefore = $this->__urlChunk(); + $urlBefore = isset($urlBefore[0]) ? $urlBefore[0] : ''; + $urlBeforeT = __t($urlBefore); + + $langs = $this->Controller->Language->find('all', array('conditions' => array('status' => 1), 'order' => array('ordering' => 'ASC'))); + $installed_codes = Set::extract('/Language/code', $langs); + $lang = $this->Controller->Session->read('language'); + + Configure::write('Config.language', $lang); + + $last_i18n_urlT = __t($this->Controller->Session->read('last_i18n_url')); + + $lang = isset($this->Controller->request->params['named']['lang']) ? $this->Controller->request->params['named']['lang'] : $lang; + $lang = isset($this->Controller->request->query['lang']) && !empty($this->Controller->request->query['lang']) ? $this->Controller->request->query['lang'] : $lang; + $lang = empty($lang) ? Configure::read('Variable.default_language') : $lang; + $lang = empty($lang) || !in_array($lang, $installed_codes) || strlen($lang) != 3 ? 'eng' : $lang; + + $this->Controller->Session->write('language', $lang); + $_lang = Set::extract("/Language[code={$lang}]/..", $langs); + + if (!isset($_lang[0]['Language'])) { # not defined -> default = english + $_lang[0]['Language'] = array( + 'code' => 'eng', + 'name' => 'English', + 'native' => 'English', + 'direction' => 'ltr' + ); + } + + Configure::write('Variable.language', $_lang[0]['Language']); + Configure::write('Variable.languages', $langs); + Configure::write('Config.language', Configure::read('Variable.language.code')); + + $urlAfter = $this->__urlChunk(); + $urlAfter = isset($urlAfter[0]) ? $urlAfter[0] : ''; + $urlAfterT = __t($urlAfter); + + if ($urlBeforeT != $urlAfterT) { + $this->Controller->Session->write('last_i18n_url', $urlBefore); + $this->Controller->redirect($urlAfterT); + } + + if (isset($this->Controller->request->params['named']['lang']) || (isset($this->Controller->request->query['lang']) && !empty($this->Controller->request->query['lang']))) { + $last_i18n_url = $this->Controller->Session->read('last_i18n_url'); + + if ($last_i18n_url && $last_i18n_urlT == $urlAfterT) { + $this->Controller->redirect($last_i18n_url); + } + } + } + + public function accessCheck() { + $this->Controller->Auth->authenticate = array( + 'Form' => array( + 'fields' => array( + 'username' => 'username', + 'password' => 'password' + ), + 'userModel' => 'User.User', + 'scope' => array('User.status' => 1) + ) + ); + + $this->Controller->Auth->loginAction = array( + 'controller' => 'user', + 'action' => 'login', + 'plugin' => 'user' + ); + + $this->Controller->Auth->authError = ''; + $this->Controller->Auth->authorize = array('Controller'); + $this->Controller->Auth->loginRedirect = Router::getParam('admin') ? '/admin' : '/'; + $this->Controller->Auth->logoutRedirect = $this->Controller->Auth->loginRedirect; + $this->Controller->Auth->allowedActions = array('login', 'logout'); + $cookie = $this->Controller->Cookie->read('UserLogin'); + + if (!$this->Controller->Auth->user() && + isset($cookie['id']) && + !empty($cookie['id']) && + isset($cookie['password']) && + !empty($cookie['password']) + ) { + $User = ClassRegistry::init('User.User'); + $User->unbindFields(); + $user = $User->find('first', + array( + 'conditions' => array( + 'User.id' => @$cookie['id'], + 'User.password' => @$cookie['password'] + ) + ) + ); + + $User->bindFields(); + + if ($user) { + $this->Controller->loadModel('UsersRole'); + $session = $user['User']; + + $session['role_id'] = $this->Controller->UsersRole->find('all', + array( + 'conditions' => array('UsersRole.user_id' => $user['User']['id']), + 'fields' => array('role_id', 'user_id') + ) + ); + + $session['role_id'] = Set::extract('/UsersRole/role_id', $session['role_id']); + $session['role_id'][] = 2; #role: authenticated user + $this->Controller->Auth->login($session); + + return true; + } + } + + if ($this->isAdmin()) { + $this->Controller->Auth->allowedActions = array('*'); + } else { + $roleId = $this->Controller->Auth->user() ? $this->Controller->Auth->user('role_id') : 3; # 3: anonymous user (public) + $aro = $this->Controller->Acl->Aro->find('first', + array( + 'conditions' => array( + 'Aro.model' => 'User.Role', + 'Aro.foreign_key' => $roleId, # roles! array of ids + ), + 'recursive' => -1, + ) + ); + $aroId = $aro['Aro']['id']; + + # get current plugin ACO + $pluginNode = $this->Controller->Acl->Aco->find('first', + array( + 'conditions' => array( + 'Aco.alias' => $this->Controller->params['plugin'], + 'parent_id = ' => null + ), + 'fields' => array('alias', 'id') + ) + ); + + # get plugin controllers ACOs + $thisControllerNode = $this->Controller->Acl->Aco->find('first', + array( + 'conditions' => array( + 'alias' => $this->Controller->name, + 'parent_id' => $pluginNode['Aco']['id'] + ) + ) + ); + + if ($thisControllerNode) { + $thisControllerActions = $this->Controller->Acl->Aco->find('list', + array( + 'conditions' => array( + 'Aco.parent_id' => $thisControllerNode['Aco']['id'], + ), + 'fields' => array( + 'Aco.id', + 'Aco.alias', + ), + 'recursive' => -1, + ) + ); + $thisControllerActionsIds = array_keys($thisControllerActions); + $allowedActions = $this->Controller->Acl->Aco->Permission->find('list', + array( + 'conditions' => array( + 'Permission.aro_id' => $aroId, + 'Permission.aco_id' => $thisControllerActionsIds, + 'Permission._create' => 1, + 'Permission._read' => 1, + 'Permission._update' => 1, + 'Permission._delete' => 1, + ), + 'fields' => array('id', 'aco_id'), + 'recursive' => -1, + ) + ); + $allowedActionsIds = array_values($allowedActions); + } + + $allow = array(); + + if (isset($allowedActionsIds) && is_array($allowedActionsIds) && count($allowedActionsIds) > 0) { + foreach ($allowedActionsIds as $i => $aId){ + $allow[] = $thisControllerActions[$aId]; + } + } + + $this->Controller->Auth->allowedActions = array_merge($this->Controller->Auth->allowedActions, $allow); + } + } + + public function setTimeZone() { + return date_default_timezone_set(Configure::read('Variable.date_default_timezone')); + } + + public function loadVariables() { + $variables = Cache::read('Variable'); + + if ($variables === false) { + $this->Controller->Variable->writeCache(); + } else { + Configure::write('Variable', $variables); + } + } + +/** + * shortcut for $this->set(`title_for_layout`...) + * + * @param string $str layout title + * @return void + */ + public function title($str) { + $this->Controller->set('title_for_layout', $str); + } + +/** + * shortcut for Session setFlash + * + * @param string $msg mesagge to display + * @param string $class type of message: error, success, alert, bubble + * @return void + */ + public function flashMsg($msg, $class = 'success') { + return $this->Controller->Session->setFlash($msg, 'default', array('class' => $class)); + } + +/** + * Set crumb from url parse or add url to the links list + * + * @param mixed $url if is array then will push de formated array to the crumbs list + * else will set base crum from string parsing + * @return void + */ + public function setCrumb($url = false) { + if (is_array($url) && !empty($url)) { + if (is_array($url[0])) { + foreach ($url as $link) { + + if (empty($link)) { + continue; + } + + $push = array( + 'MenuLink' => array( + 'link_title' => $link[0], + 'router_path' => (empty($link[1]) ? 'javascript:return false;': $link[1]), + 'description' => (isset($link[2]) ? $link[2] : ''), + ) + ); + + $this->Controller->viewVars['breadCrumb'][] = $push; + } + } else { + $push = array( + 'MenuLink' => array( + 'link_title' => $url[0], + 'router_path' => (empty($url[1]) ? 'javascript:return false;': $url[1]), + 'description' => (isset($url[2]) ? $url[2] : ''), + ) + ); + $this->Controller->viewVars['breadCrumb'][] = $push; + } + + return; + } else { + $url = !is_string($url) ? $this->__urlChunk() : $url; + } + + if (is_array($url)) { + foreach ($url as $k => $u) { + $url[$k] = preg_replace('/\/{2,}/', '', "{$u}//"); + } + } else { + $url = preg_replace('/\/{2,}/', '', "{$url}//"); + } + + $this->Controller->set('breadCrumb', array()); + + $current = $this->Controller->MenuLink->find('first', + array( + 'conditions' => array( + 'MenuLink.router_path' => (empty($url) ? '': $url) + ) + ) + ); + + if (!empty($current)) { + $this->Controller->MenuLink->Behaviors->detach('Tree'); + $this->Controller->MenuLink->Behaviors->attach('Tree', + array( + 'parent' => 'parent_id', + 'left' => 'lft', + 'right' => 'rght', + 'scope' => "MenuLink.menu_id = '{$current['MenuLink']['menu_id']}'" + ) + ); + + $path = $this->Controller->MenuLink->getPath($current['MenuLink']['id']); + + if (isset($path[0]['MenuLink']['link_title'])) { + $path[0]['MenuLink']['link_title'] = __t($path[0]['MenuLink']['link_title']); + } + + $this->Controller->set('breadCrumb', $path); + + } + + return; + } + +/** + * Insert custom block in stack + * + * @param array $data formatted block array + * @param string $region theme region where to push + * @return boolean + */ + public function blockPush($block = array(), $region = null, $show_on = true) { + if (!$show_on) { + return; + } + + $_block = array( + 'title' => '', + 'pages' => '', + 'visibility' => 0, + 'body' => '', # + 'region' => null, + 'format' => null # + ); + + $block = array_merge($_block, $block); + $block['module'] = null; + $block['id'] = null; + $block['delta'] = null; + + if (!is_null($region)) { + $block['region'] = $region; + } + + if (empty($block['region']) || empty($block['body'])) { + return false; + } + + $__block = $block; + + unset($__block['format'], $__block['body'], $__block['region'], $__block['theme']); + + $Block = array( + 'Block' => $__block, + 'BlockCustom' => array( + 'body' => $block['body'], + 'format' => $block['format'] + ), + 'BlockRegion' => array( + 0 => array( + 'theme' => $this->Controller->theme, + 'region' => $block['region'] + ) + ) + ); + + $this->Controller->Layout['blocks'][] = $Block; + } + + public function loadModules() { + $modules = Cache::read('Modules'); + + if ($modules === false) { + $modules = $this->Controller->Module->find('all', array('recursive' => -1)); + + foreach ($modules as $m) { + $v = $m['Module']; + + CakePlugin::load($m['Module']['name']); + + $v['path'] = App::pluginPath($m['Module']['name']); + $yamlFile = (strpos($m['Module']['name'], 'theme_') !== false) ? dirname(dirname($v['path'])) . DS . basename(dirname(dirname($v['path']))) . '.yaml' : $v['path'] . "{$m['Module']['name']}.yaml"; + $v['yaml'] = file_exists($yamlFile) ? Spyc::YAMLLoad($yamlFile) : array(); + + Configure::write('Modules.' . $m['Module']['name'], $v); + } + + Cache::write('Modules', Configure::read('Modules')); + } else { + Configure::write('Modules', $modules); + } + } + + public function isAdmin() { + return ($this->Controller->Auth->user() && in_array(1, (array)$this->Controller->Auth->user('role_id'))); + } + + private function __urlChunk() { + $url = '/' . $this->Controller->request->url; + $out = array(); + + $out[] = $url; + + foreach ($this->Controller->request->params['named'] as $key => $val) { + $url = str_replace_once("/{$key}:{$val}", '', $url); + $out[] = $url; + } + + $out[] = $url; + + if ($this->Controller->request->params['controller'] == $this->plugin) { + $url = str_replace_once("/{$this->Controller->request->params['controller']}", '', $url); + $out[] = $url; + } else if ($this->Controller->request->params['action'] == 'index' || $this->Controller->request->params['action'] == 'admin_index') { + $url = str_replace_once("/index", '', $url); + $out[] = $url; + } + + foreach ($this->Controller->request->params['pass'] as $p) { + $url = str_replace_once("/{$p}", '', $url); + $out[] = $url; + } + + return array_unique($out); + } +} \ No newline at end of file diff --git a/app/Controller/InstallController.php b/app/Controller/InstallController.php new file mode 100644 index 00000000..21dcecc6 --- /dev/null +++ b/app/Controller/InstallController.php @@ -0,0 +1,297 @@ + + * @link http://cms.quickapps.es + */ +App::uses('Controller', 'Controller'); +class InstallController extends Controller { + public $name = 'Install'; + public $uses = array(); + public $components = array('Session'); + public $helpers = array('Layout', 'Html', 'Form'); + private $__defaultDbConfig = array( + 'name' => 'default', + 'datasource'=> 'Database/Mysql', + 'persistent'=> false, + 'host'=> 'localhost', + 'login'=> 'root', + 'password'=> '', + 'database'=> 'quickapps', + 'schema'=> null, + 'prefix'=> 'qa_', + 'encoding' => 'UTF8', + 'port' => '3306' + ); + + public function beforeFilter() { + $this->viewClass = 'View'; + $this->layout = 'install'; + + # already installed ? + if (file_exists(APP . DS . 'Config' . DS . 'database.php') && file_exists(APP . DS . 'Config' . DS . 'install')) { + $this->redirect('/'); + } + } + + public function index() { + $this->redirect('/install/license'); + } + + /* Step 1: License agreement */ + public function license() { + if (isset($this->data['License'])) { + $this->__stepSuccess('license'); + $this->redirect('/install/server_test'); + } + } + + /* Step 2: Server test */ + public function server_test() { + if (!$this->__stepSuccess('license', true)) { + $this->redirect('/install/license'); + } + + if (!empty($this->data['Test'])) { + $this->__stepSuccess('server_test'); + $this->redirect('/install/database'); + } + + $tests = array( + 'php' => array( + 'test' => version_compare(PHP_VERSION, '5.2.6', '>='), + 'msg' => __t('Your php version is not suported. check that your version is 5.2 or newer.') + ), + 'mysql' => array( + 'test' => (extension_loaded('mysql') || extension_loaded('mysqli')), + 'msg' => __t('MySQL extension is not loaded on your server.') + ), + 'no_safe_mode' => array( + 'test' => (ini_get('safe_mode') == false || ini_get('safe_mode') == '' || strtolower(ini_get('safe_mode')) == 'off'), + 'msg' => __t('Your server has SafeMode on, please turn it off before continue.') + ), + 'tmp_writable' => array( + 'test' => is_writable(APP . DS . 'tmp'), + 'msg' => __t('APP/tmp folder is not writable') + ), + 'cache_writable' => array( + 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache'), + 'msg' => __t('APP/tmp/cache folder is not writable') + ), + 'installer_writable' => array( + 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'installer'), + 'msg' => __t('APP/tmp/cache/installer folder is not writable') + ), + 'models_writable' => array( + 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'models'), + 'msg' => __t('APP/tmp/cache/models folder is not writable') + ), + 'persistent_writable' => array( + 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'persistent'), + 'msg' => __t('APP/tmp/cache/persistent folder is not writable') + ), + 'i18n_writable' => array( + 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'i18n'), + 'msg' => __t('APP/tmp/cache/i18n folder is not writable') + ), + 'Config_writable' => array( + 'test' => is_writable(APP . DS . 'Config'), + 'msg' => __t('APP/Config folder is not writable') + ), + 'core.php_writable' => array( + 'test' => is_writable(APP . DS . 'Config'), + 'msg' => __t('APP/Config/core.php file is not writable') + ) + ); + + $results = array_unique(Set::extract('{s}.test', $tests)); + + if (!(count($results) === 1 && $results[0] === true)) { + $this->set('success', false); + $this->set('tests', $tests); + } else { + $this->set('success', true); + } + } + + /* Step 3: Database */ + public function database() { + if (!$this->__stepSuccess(array('license', 'server_test'), true)) { + $this->redirect('/install/license'); + } + + if (!empty($this->data['Database'])) { + copy(APP . 'Config' . DS . 'database.php.install', APP . 'Config' . DS . 'database.php'); + App::import('Utility', 'File'); + + $file = new File(APP . 'Config' . DS . 'database.php', true); + $dbSettings = $file->read(); + $data = $this->data; + $data['Database']['datasource'] = 'Database/Mysql'; + $data['Database']['persistent'] = 'false'; + + $dbSettings = str_replace( + array( + '{db_datasource}', + '{db_persistent}', + '{db_host}', + '{db_login}', + '{db_password}', + '{db_database}', + '{db_prefix}' + ), + array( + $data['Database']['datasource'], + $data['Database']['persistent'], + $data['Database']['host'], + $data['Database']['login'], + $data['Database']['password'], + $data['Database']['database'], + $data['Database']['prefix'] + ), + $dbSettings + ); + + $this->__defaultDbConfig = Set::merge($this->__defaultDbConfig, $data['Database']); + + if ($file->write($dbSettings)) { + $MySQLConn = @mysql_connect($this->__defaultDbConfig['host'] . ':' . $this->__defaultDbConfig['port'], $this->__defaultDbConfig['login'], $this->__defaultDbConfig['password'], true); + + if (@mysql_select_db($this->__defaultDbConfig['database'], $MySQLConn)) { + @App::import('Model', 'ConnectionManager'); + @ConnectionManager::create('default'); + + $db = ConnectionManager::getDataSource('default', $this->__defaultDbConfig); + $folder = new Folder(APP . 'Config' . DS . 'Schema' . DS . 'tables' . DS); + $files = $folder->read(); + $files = $files[1]; + $execute = array(); + + foreach ($files as $sql_file) { + $file = new File(APP . 'Config' . DS . 'Schema' . DS . 'tables' . DS . $sql_file); + $sql = $file->read(); + $query = $this->__prepareDump($sql); + $execute[] = $db->execute(str_replace('#__', $data['Database']['prefix'], $query)); + } + + if (!in_array(false, array_values($execute), true)) { + # random keys values + $file = new File(APP . 'Config' . DS . 'core.php'); + + App::uses('Security', 'Utility'); + App::load('Security'); + + $salt = Security::generateAuthKey(); + $seed = mt_rand() . mt_rand(); + $contents = $file->read(); + $contents = preg_replace('/(?<=Configure::write\(\'Security.salt\', \')([^\' ]+)(?=\'\))/', $salt, $contents); + $contents = preg_replace('/(?<=Configure::write\(\'Security.cipherSeed\', \')(\d+)(?=\'\))/', $seed, $contents); + + $file->write($contents); + Cache::write('QaInstallDatabase', 'success'); # fix: Security keys change + $this->redirect('/install/user_account'); + } else { + $this->Session->setFlash(__t('Could not dump database'), 'default', 'error'); + } + } else { + $file->close(); + unlink(APP . 'Config' . DS . 'database.php'); + $this->Session->setFlash(__t('Could not connect to database.'), 'default', 'error'); + } + } else { + $this->Session->setFlash(__t('Could not write database.php file.'), 'default', 'error'); + } + } + } + + /* Step 4: User account */ + public function user_account() { + if (Cache::read('QaInstallDatabase') == 'success' || + $this->__stepSuccess(array('license', 'server_test', 'database'), true) + ) { + $this->__stepSuccess('license'); + $this->__stepSuccess('server_test'); + $this->__stepSuccess('database'); + + Cache::delete('QaInstallDatabase'); + } else { + $this->redirect('/install/license'); + } + + if (isset($this->data['User'])) { + $this->loadModel('User.User'); + $data = $this->data; + $data['User']['status'] = 1; + $data['Role']['Role'] = array(1); + + if ($this->User->save($data)) { + $this->__stepSuccess('user_account'); + $this->redirect('/install/finish'); + } else { + $errors = ''; + + foreach ($this->User->invalidFields() as $field => $error) { + $errors .= "{$field}: {$error}
"; + } + + $this->Session->setFlash( + '' . __t('Could not create new user, please try again.') . "
" . + $errors + , 'default', 'error'); + } + } + } + + /* Step 5: Finish */ + public function finish() { + if (!$this->__stepSuccess(array('license', 'server_test', 'database', 'user_account'), true)) { + $this->redirect('/install/license'); + } + + App::import('Utility', 'File'); + + $file = new File(APP . 'Config' . DS . 'install', true); + + if ($file->write(time())) { + $this->__stepSuccess('finish'); + $this->Session->delete('QaInstall'); + $this->redirect('/admin'); + } else { + $this->Session->setFlash(__t("Could not write 'install' file. Check file/folder permissions and refresh this page"), 'default', 'error'); + } + } + + private function __stepSuccess($step, $check = false) { + if (!$check) { + return $this->Session->write("QaInstall.{$step}", 'success'); + } + + if (is_array($step)) { + foreach ($step as $s) { + if (!$this->Session->check("QaInstall.{$s}")) { + return false; + } + } + + return true; + } else { + return $this->Session->check("QaInstall.{$step}"); + } + + return false; + } + + private function __prepareDump($sql) { + $sql = trim($sql); + $sql = ereg_replace("\n#[^\n]*\n", "\n", $sql); + $sql = preg_replace('/^\-\-(.*)/im', '', $sql); + $sql = preg_replace("/\n{2,}/m", "\n", $sql); + + return $sql; + } +} \ No newline at end of file diff --git a/app/Lib/Model/Model.php b/app/Lib/Model/Model.php new file mode 100644 index 00000000..d054544d --- /dev/null +++ b/app/Lib/Model/Model.php @@ -0,0 +1,3429 @@ + table 'users'; class 'Man' => table 'men') + * The table is required to have at least 'id auto_increment' primary key. + * + * @package Cake.Model + * @link http://book.cakephp.org/view/1000/Models + */ +class Model extends Object { + +/** + * The name of the DataSource connection that this Model uses + * + * The value must be an attribute name that you defined in `app/Config/database.php` + * or created using `ConnectionManager::create()`. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#useDbConfig-1058 + */ + public $useDbConfig = 'default'; + +/** + * Custom database table name, or null/false if no table association is desired. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#useTable-1059 + */ + public $useTable = null; + +/** + * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements. + * + * This field is also used in `find('list')` when called with no extra parameters in the fields list + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#displayField-1062 + */ + public $displayField = null; + +/** + * Value of the primary key ID of the record that this model is currently pointing to. + * Automatically set after database insertions. + * + * @var mixed + */ + public $id = false; + +/** + * Container for the data that this model gets from persistent storage (usually, a database). + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#data-1065 + */ + public $data = array(); + +/** + * Table name for this Model. + * + * @var string + */ + public $table = false; + +/** + * The name of the primary key field for this model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#primaryKey-1061 + */ + public $primaryKey = null; + +/** + * Field-by-field table metadata. + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#_schema-1066 + */ + protected $_schema = null; + +/** + * List of validation rules. It must be an array with the field name as key and using + * as value one of the following possibilities + * + * ### Validating using regular expressions + * + * {{{ + * public $validate = array( + * 'name' => '/^[a-z].+$/i' + * ); + * }}} + * + * ### Validating using methods (no parameters) + * + * {{{ + * public $validate = array( + * 'name' => 'notEmpty' + * ); + * }}} + * + * ### Validating using methods (with parameters) + * + * {{{ + * public $validate = array( + * 'age' => array( + * 'rule' => array('between', 5, 25) + * ) + * ); + * }}} + * + * ### Validating using custom method + * + * {{{ + * public $validate = array( + * 'password' => array( + * 'rule' => array('customValidation') + * ) + * ); + * public function customValidation($data) { + * // $data will contain array('password' => 'value') + * if (isset($this->data[$this->alias]['password2'])) { + * return $this->data[$this->alias]['password2'] === current($data); + * } + * return true; + * } + * }}} + * + * ### Validations with messages + * + * The messages will be used in Model::$validationErrors and can be used in the FormHelper + * + * {{{ + * public $validate = array( + * 'age' => array( + * 'rule' => array('between', 5, 25), + * 'message' => array('The age must be between %d and %d.') + * ) + * ); + * }}} + * + * ### Multiple validations to the same field + * + * {{{ + * public $validate = array( + * 'login' => array( + * array( + * 'role' => 'alphaNumeric', + * 'message' => 'Only alphabets and numbers allowed', + * 'last' => true + * ), + * array( + * 'role' => array('minLength', 8), + * 'message' => array('Minimum length of %d characters') + * ) + * ) + * ); + * }}} + * + * ### Valid keys in validations + * + * - `role`: String with method name, regular expression (started by slash) or array with method and parameters + * - `message`: String with the message or array if have multiple parameters. See http://php.net/sprintf + * - `last`: Boolean value to indicate if continue validating the others rules if the current fail [Default: true] + * - `required`: Boolean value to indicate if the field must be present on save + * - `allowEmpty`: Boolean value to indicate if the field can be empty + * - `on`: Possible values: `update`, `create`. Indicate to apply this rule only on update or create + * + * @var array + * @link http://book.cakephp.org/view/1057/Model-Attributes#validate-1067 + * @link http://book.cakephp.org/view/1143/Data-Validation + */ + public $validate = array(); + +/** + * List of validation errors. + * + * @var array + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public $validationErrors = array(); + + +/** + * Name of the validation string domain to use when translating validation errors. + * + * @var string + */ + public $validationDomain = null; + +/** + * Database table prefix for tables in model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#tablePrefix-1060 + */ + public $tablePrefix = null; + +/** + * Name of the model. + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#name-1068 + */ + public $name = null; + +/** + * Alias name for model. + * + * @var string + */ + public $alias = null; + +/** + * List of table names included in the model description. Used for associations. + * + * @var array + */ + public $tableToModel = array(); + +/** + * Whether or not to cache queries for this model. This enables in-memory + * caching only, the results are not stored beyond the current request. + * + * @var boolean + * @link http://book.cakephp.org/view/1057/Model-Attributes#cacheQueries-1069 + */ + public $cacheQueries = false; + +/** + * Detailed list of belongsTo associations. + * + * ### Basic usage + * + * `public $belongsTo = array('Group', 'Department');` + * + * ### Detailed configuration + * + * {{{ + * public $belongsTo = array( + * 'Group', + * 'Department' => array( + * 'className' => 'Department', + * 'foreignKey' => 'department_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘Profile belongsTo User’ relationship, the className key should equal ‘User.’ + * - `foreignKey`: the name of the foreign key found in the current model. This is + * especially handy if you need to define multiple belongsTo relationships. The default + * value for this key is the underscored, singular name of the other model, suffixed with ‘_id’. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “User.active = 1†is always + * better than just “active = 1.†+ * - `type`: the type of the join to use in the SQL query, default is LEFT which + * may not fit your needs in all situations, INNER may be helpful when you want + * everything from your main and associated models or nothing at all!(effective + * when used with some conditions of course). (NB: type value is in lower case - i.e. left, inner) + * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `counterCache`: If set to true the associated Model will automatically increase or + * decrease the “[singular_model_name]_count†field in the foreign table whenever you do + * a save() or delete(). If its a string then its the field name to use. The value in the + * counter field represents the number of related rows. + * - `counterScope`: Optional conditions array to use for updating counter cache field. + * + * @var array + * @link http://book.cakephp.org/view/1042/belongsTo + */ + public $belongsTo = array(); + +/** + * Detailed list of hasOne associations. + * + * ### Basic usage + * + * `public $hasOne = array('Profile', 'Address');` + * + * ### Detailed configuration + * + * {{{ + * public $hasOne = array( + * 'Profile', + * 'Address' => array( + * 'className' => 'Address', + * 'foreignKey' => 'user_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘User hasOne Profile’ relationship, the className key should equal ‘Profile.’ + * - `foreignKey`: the name of the foreign key found in the other model. This is + * especially handy if you need to define multiple hasOne relationships. + * The default value for this key is the underscored, singular name of the + * current model, suffixed with ‘_id’. In the example above it would default to 'user_id'. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “Profile.approved = 1†is + * always better than just “approved = 1.†+ * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `dependent`: When the dependent key is set to true, and the model’s delete() + * method is called with the cascade parameter set to true, associated model + * records are also deleted. In this case we set it true so that deleting a + * User will also delete her associated Profile. + * + * @var array + * @link http://book.cakephp.org/view/1041/hasOne + */ + public $hasOne = array(); + +/** + * Detailed list of hasMany associations. + * + * ### Basic usage + * + * `public $hasMany = array('Comment', 'Task');` + * + * ### Detailed configuration + * + * {{{ + * public $hasMany = array( + * 'Comment', + * 'Task' => array( + * 'className' => 'Task', + * 'foreignKey' => 'user_id' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you’re defining a ‘User hasMany Comment’ relationship, the className key should equal ‘Comment.’ + * - `foreignKey`: the name of the foreign key found in the other model. This is + * especially handy if you need to define multiple hasMany relationships. The default + * value for this key is the underscored, singular name of the actual model, suffixed with ‘_id’. + * - `conditions`: An SQL fragment used to filter related model records. It’s good + * practice to use model names in SQL fragments: “Comment.status = 1†is always + * better than just “status = 1.†+ * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `limit`: The maximum number of associated rows you want returned. + * - `offset`: The number of associated rows to skip over (given the current + * conditions and order) before fetching and associating. + * - `dependent`: When dependent is set to true, recursive model deletion is + * possible. In this example, Comment records will be deleted when their + * associated User record has been deleted. + * - `exclusive`: When exclusive is set to true, recursive model deletion does + * the delete with a deleteAll() call, instead of deleting each entity separately. + * This greatly improves performance, but may not be ideal for all circumstances. + * - `finderQuery`: A complete SQL query CakePHP can use to fetch associated model + * records. This should be used in situations that require very custom results. + * + * @var array + * @link http://book.cakephp.org/view/1043/hasMany + */ + public $hasMany = array(); + +/** + * Detailed list of hasAndBelongsToMany associations. + * + * ### Basic usage + * + * `public $hasAndBelongsToMany = array('Role', 'Address');` + * + * ### Detailed configuration + * + * {{{ + * public $hasAndBelongsToMany = array( + * 'Role', + * 'Address' => array( + * 'className' => 'Address', + * 'foreignKey' => 'user_id', + * 'associationForeignKey' => 'address_id', + * 'joinTable' => 'addresses_users' + * ) + * ); + * }}} + * + * ### Possible keys in association + * + * - `className`: the classname of the model being associated to the current model. + * If you're defining a ‘Recipe HABTM Tag' relationship, the className key should equal ‘Tag.' + * - `joinTable`: The name of the join table used in this association (if the + * current table doesn't adhere to the naming convention for HABTM join tables). + * - `with`: Defines the name of the model for the join table. By default CakePHP + * will auto-create a model for you. Using the example above it would be called + * RecipesTag. By using this key you can override this default name. The join + * table model can be used just like any "regular" model to access the join table directly. + * - `foreignKey`: the name of the foreign key found in the current model. + * This is especially handy if you need to define multiple HABTM relationships. + * The default value for this key is the underscored, singular name of the + * current model, suffixed with ‘_id'. + * - `associationForeignKey`: the name of the foreign key found in the other model. + * This is especially handy if you need to define multiple HABTM relationships. + * The default value for this key is the underscored, singular name of the other + * model, suffixed with ‘_id'. + * - `unique`: If true (default value) cake will first delete existing relationship + * records in the foreign keys table before inserting new ones, when updating a + * record. So existing associations need to be passed again when updating. + * - `conditions`: An SQL fragment used to filter related model records. It's good + * practice to use model names in SQL fragments: "Comment.status = 1" is always + * better than just "status = 1." + * - `fields`: A list of fields to be retrieved when the associated model data is + * fetched. Returns all fields by default. + * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. + * - `limit`: The maximum number of associated rows you want returned. + * - `offset`: The number of associated rows to skip over (given the current + * conditions and order) before fetching and associating. + * - `finderQuery`, `deleteQuery`, `insertQuery`: A complete SQL query CakePHP + * can use to fetch, delete, or create new associated model records. This should + * be used in situations that require very custom results. + * + * @var array + * @link http://book.cakephp.org/view/1044/hasAndBelongsToMany-HABTM + */ + public $hasAndBelongsToMany = array(); + +/** + * List of behaviors to load when the model object is initialized. Settings can be + * passed to behaviors by using the behavior name as index. Eg: + * + * public $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1')) + * + * @var array + * @link http://book.cakephp.org/view/1072/Using-Behaviors + */ + public $actsAs = null; + +/** + * Holds the Behavior objects currently bound to this model. + * + * @var BehaviorCollection + */ + public $Behaviors = null; + +/** + * Whitelist of fields allowed to be saved. + * + * @var array + */ + public $whitelist = array(); + +/** + * Whether or not to cache sources for this model. + * + * @var boolean + */ + public $cacheSources = true; + +/** + * Type of find query currently executing. + * + * @var string + */ + public $findQueryType = null; + +/** + * Number of associations to recurse through during find calls. Fetches only + * the first level by default. + * + * @var integer + * @link http://book.cakephp.org/view/1057/Model-Attributes#recursive-1063 + */ + public $recursive = 1; + +/** + * The column name(s) and direction(s) to order find results by default. + * + * public $order = "Post.created DESC"; + * public $order = array("Post.view_count DESC", "Post.rating DESC"); + * + * @var string + * @link http://book.cakephp.org/view/1057/Model-Attributes#order-1064 + */ + public $order = null; + +/** + * Array of virtual fields this model has. Virtual fields are aliased + * SQL expressions. Fields added to this property will be read as other fields in a model + * but will not be saveable. + * + * `public $virtualFields = array('two' => '1 + 1');` + * + * Is a simplistic example of how to set virtualFields + * + * @var array + */ + public $virtualFields = array(); + +/** + * Default list of association keys. + * + * @var array + */ + protected $_associationKeys = array( + 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'), + 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'), + 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'), + 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery') + ); + +/** + * Holds provided/generated association key names and other data for all associations. + * + * @var array + */ + protected $_associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + +/** + * Holds model associations temporarily to allow for dynamic (un)binding. + * + * @var array + */ + public $__backAssociation = array(); + +/** + * Back inner association + * + * @var array + */ + public $__backInnerAssociation = array(); + +/** + * Back original association + * + * @var array + */ + public $__backOriginalAssociation = array(); + +/** + * Back containable association + * + * @var array + */ + public $__backContainableAssociation = array(); + +/** + * The ID of the model record that was last inserted. + * + * @var integer + */ + protected $_insertID = null; + +/** + * Has the datasource been configured. + * + * @var boolean + * @see Model::getDataSource + */ + protected $_sourceConfigured = false; + +/** + * List of valid finder method options, supplied as the first parameter to find(). + * + * @var array + */ + public $findMethods = array( + 'all' => true, 'first' => true, 'count' => true, + 'neighbors' => true, 'list' => true, 'threaded' => true + ); + +/** + * Constructor. Binds the model's database table to the object. + * + * If `$id` is an array it can be used to pass several options into the model. + * + * - id - The id to start the model on. + * - table - The table to use for this model. + * - ds - The connection name this model is connected to. + * - name - The name of the model eg. Post. + * - alias - The alias of the model, this is used for registering the instance in the `ClassRegistry`. + * eg. `ParentThread` + * + * ### Overriding Model's __construct method. + * + * When overriding Model::__construct() be careful to include and pass in all 3 of the + * arguments to `parent::__construct($id, $table, $ds);` + * + * ### Dynamically creating models + * + * You can dynamically create model instances using the $id array syntax. + * + * {{{ + * $Post = new Model(array('table' => 'posts', 'name' => 'Post', 'ds' => 'connection2')); + * }}} + * + * Would create a model attached to the posts table on connection2. Dynamic model creation is useful + * when you want a model object that contains no associations or attached behaviors. + * + * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above. + * @param string $table Name of database table to use. + * @param string $ds DataSource connection name. + */ + public function __construct($id = false, $table = null, $ds = null) { + parent::__construct(); + + if (is_array($id)) { + extract(array_merge( + array( + 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig, + 'name' => $this->name, 'alias' => $this->alias + ), + $id + )); + } + + if ($this->name === null) { + $this->name = (isset($name) ? $name : get_class($this)); + } + + if ($this->alias === null) { + $this->alias = (isset($alias) ? $alias : $this->name); + } + + if ($this->primaryKey === null) { + $this->primaryKey = 'id'; + } + + ClassRegistry::addObject($this->alias, $this); + + $this->id = $id; + unset($id); + + if ($table === false) { + $this->useTable = false; + } elseif ($table) { + $this->useTable = $table; + } + + if ($ds !== null) { + $this->useDbConfig = $ds; + } + + if (is_subclass_of($this, 'AppModel')) { + $merge = array('findMethods'); + if ($this->actsAs !== null || $this->actsAs !== false) { + $merge[] = 'actsAs'; + } + $parentClass = get_parent_class($this); + if ($parentClass !== 'AppModel') { + $this->_mergeVars($merge, $parentClass); + } + $this->_mergeVars($merge, 'AppModel'); + } + $this->Behaviors = new BehaviorCollection(); + + if ($this->useTable !== false) { + + if ($this->useTable === null) { + $this->useTable = Inflector::tableize($this->name); + } + + if ($this->displayField == null) { + unset($this->displayField); + } + $this->table = $this->useTable; + $this->tableToModel[$this->table] = $this->alias; + } elseif ($this->table === false) { + $this->table = Inflector::tableize($this->name); + } + $this->_createLinks(); + $this->Behaviors->init($this->alias, $this->actsAs); + } + +/** + * Handles custom method calls, like findBy for DB models, + * and custom RPC calls for remote data sources. + * + * @param string $method Name of method to call. + * @param array $params Parameters for the method. + * @return mixed Whatever is returned by called method + */ + public function __call($method, $params) { + $result = $this->Behaviors->dispatchMethod($this, $method, $params); + if ($result !== array('unhandled')) { + return $result; + } + $return = $this->getDataSource()->query($method, $params, $this); + return $return; + } + +/** + * Handles the lazy loading of model associations by lookin in the association arrays for the requested variable + * + * @param string $name variable tested for existance in class + * @return boolean true if the variable exists (if is a not loaded model association it will be created), false otherwise + */ + public function __isset($name) { + $className = false; + + foreach ($this->_associations as $type) { + if (isset($name, $this->{$type}[$name])) { + $className = empty($this->{$type}[$name]['className']) ? $name : $this->{$type}[$name]['className']; + break; + } else if ($type == 'hasAndBelongsToMany') { + foreach ($this->{$type} as $k => $relation) { + if (empty($relation['with'])) { + continue; + } + if (is_array($relation['with'])) { + if (key($relation['with']) === $name) { + $className = $name; + } + } else { + list($plugin, $class) = pluginSplit($relation['with']); + if ($class === $name) { + $className = $relation['with']; + } + } + if ($className) { + $assocKey = $k; + $dynamic = !empty($relation['dynamicWith']); + break(2); + } + } + } + } + + if (!$className) { + return false; + } + + list($plugin, $className) = pluginSplit($className); + + if (!ClassRegistry::isKeySet($className) && !empty($dynamic)) { + $this->{$className} = new AppModel(array( + 'name' => $className, + 'table' => $this->hasAndBelongsToMany[$assocKey]['joinTable'], + 'ds' => $this->useDbConfig + )); + } else { + $this->_constructLinkedModel($name, $className, $plugin); + } + + if (!empty($assocKey)) { + $this->hasAndBelongsToMany[$assocKey]['joinTable'] = $this->{$name}->table; + if (count($this->{$name}->schema()) <= 2 && $this->{$name}->primaryKey !== false) { + $this->{$name}->primaryKey = $this->hasAndBelongsToMany[$assocKey]['foreignKey']; + } + } + + return true; + } + +/** + * Returns the value of the requested variable if it can be set by __isset() + * + * @param string $name variable requested for it's value or reference + * @return mixed value of requested variable if it is set + */ + public function __get($name) { + if ($name === 'displayField') { + return $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey)); + } + if (isset($this->{$name})) { + return $this->{$name}; + } + } + +/** + * Bind model associations on the fly. + * + * If `$reset` is false, association will not be reset + * to the originals defined in the model + * + * Example: Add a new hasOne binding to the Profile model not + * defined in the model source code: + * + * `$this->User->bindModel( array('hasOne' => array('Profile')) );` + * + * Bindings that are not made permanent will be reset by the next Model::find() call on this + * model. + * + * @param array $params Set of bindings (indexed by binding type) + * @param boolean $reset Set to false to make the binding permanent + * @return boolean Success + * @link http://book.cakephp.org/view/1045/Creating-and-Destroying-Associations-on-the-Fly + */ + public function bindModel($params, $reset = true) { + foreach ($params as $assoc => $model) { + if ($reset === true && !isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + foreach ($model as $key => $value) { + $assocName = $key; + + if (is_numeric($key)) { + $assocName = $value; + $value = array(); + } + $modelName = $assocName; + $this->{$assoc}[$assocName] = $value; + if (property_exists($this, $assocName)) { + unset($this->{$assocName}); + } + if ($reset === false && isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc][$assocName] = $value; + } + } + } + $this->_createLinks(); + return true; + } + +/** + * Turn off associations on the fly. + * + * If $reset is false, association will not be reset + * to the originals defined in the model + * + * Example: Turn off the associated Model Support request, + * to temporarily lighten the User model: + * + * `$this->User->unbindModel( array('hasMany' => array('Supportrequest')) );` + * + * unbound models that are not made permanent will reset with the next call to Model::find() + * + * @param array $params Set of bindings to unbind (indexed by binding type) + * @param boolean $reset Set to false to make the unbinding permanent + * @return boolean Success + * @link http://book.cakephp.org/view/1045/Creating-and-Destroying-Associations-on-the-Fly + */ + public function unbindModel($params, $reset = true) { + foreach ($params as $assoc => $models) { + if ($reset === true && !isset($this->__backAssociation[$assoc])) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + foreach ($models as $model) { + if ($reset === false && isset($this->__backAssociation[$assoc][$model])) { + unset($this->__backAssociation[$assoc][$model]); + } + unset($this->{$assoc}[$model]); + } + } + return true; + } + +/** + * Create a set of associations. + * + * @return void + */ + protected function _createLinks() { + foreach ($this->_associations as $type) { + if (!is_array($this->{$type})) { + $this->{$type} = explode(',', $this->{$type}); + + foreach ($this->{$type} as $i => $className) { + $className = trim($className); + unset ($this->{$type}[$i]); + $this->{$type}[$className] = array(); + } + } + + if (!empty($this->{$type})) { + foreach ($this->{$type} as $assoc => $value) { + $plugin = null; + + if (is_numeric($assoc)) { + unset ($this->{$type}[$assoc]); + $assoc = $value; + $value = array(); + $this->{$type}[$assoc] = $value; + + if (strpos($assoc, '.') !== false) { + list($plugin, $assoc) = pluginSplit($assoc); + $this->{$type}[$assoc] = array('className' => $plugin. '.' . $assoc); + } + } + $this->_generateAssociation($type, $assoc); + } + } + } + } + +/** + * Protected helper method to create associated models of a given class. + * + * @param string $assoc Association name + * @param string $className Class name + * @param string $plugin name of the plugin where $className is located + * examples: public $hasMany = array('Assoc' => array('className' => 'ModelName')); + * usage: $this->Assoc->modelMethods(); + * + * public $hasMany = array('ModelName'); + * usage: $this->ModelName->modelMethods(); + * @return void + */ + protected function _constructLinkedModel($assoc, $className = null, $plugin = null) { + if (empty($className)) { + $className = $assoc; + } + + if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) { + if ($plugin) { + $plugin .= '.'; + } + $model = array('class' => $plugin . $className, 'alias' => $assoc); + $this->{$assoc} = ClassRegistry::init($model); + if ($plugin) { + ClassRegistry::addObject($plugin . $className, $this->{$assoc}); + } + if ($assoc) { + $this->tableToModel[$this->{$assoc}->table] = $assoc; + } + } + } + +/** + * Build an array-based association from string. + * + * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' + * @param string $assocKey + * @return void + */ + protected function _generateAssociation($type, $assocKey) { + $class = $assocKey; + $dynamicWith = false; + + foreach ($this->_associationKeys[$type] as $key) { + + if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) { + $data = ''; + + switch ($key) { + case 'fields': + $data = ''; + break; + + case 'foreignKey': + $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id'; + break; + + case 'associationForeignKey': + $data = Inflector::singularize($this->{$class}->table) . '_id'; + break; + + case 'with': + $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable'])); + $dynamicWith = true; + break; + + case 'joinTable': + $tables = array($this->table, $this->{$class}->table); + sort ($tables); + $data = $tables[0] . '_' . $tables[1]; + break; + + case 'className': + $data = $class; + break; + + case 'unique': + $data = true; + break; + } + $this->{$type}[$assocKey][$key] = $data; + } + + if ($dynamicWith) { + $this->{$type}[$assocKey]['dynamicWith'] = true; + } + + } + } + +/** + * Sets a custom table for your controller class. Used by your controller to select a database table. + * + * @param string $tableName Name of the custom table + * @throws MissingTableException when database table $tableName is not found on data source + * @return void + */ + public function setSource($tableName) { + $this->setDataSource($this->useDbConfig); + $db = ConnectionManager::getDataSource($this->useDbConfig); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + + if (method_exists($db, 'listSources')) { + $sources = $db->listSources(); + if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) { + throw new MissingTableException(array( + 'table' => $this->tablePrefix . $tableName, + 'class' => $this->alias + )); + } + $this->_schema = null; + } + $this->table = $this->useTable = $tableName; + $this->tableToModel[$this->table] = $this->alias; + } + +/** + * This function does two things: + * + * 1. it scans the array $one for the primary key, + * and if that's found, it sets the current id to the value of $one[id]. + * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object. + * 2. Returns an array with all of $one's keys and values. + * (Alternative indata: two strings, which are mangled to + * a one-item, two-dimensional array using $one for a key and $two as its value.) + * + * @param mixed $one Array or string of data + * @param string $two Value string for the alternative indata method + * @return array Data with all of $one's keys and values + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function set($one, $two = null) { + if (!$one) { + return; + } + if (is_object($one)) { + if ($one instanceof SimpleXMLElement || $one instanceof DOMNode) { + $one = $this->_normalizeXmlData(Xml::toArray($one)); + } else { + $one = Set::reverse($one); + } + } + + if (is_array($one)) { + $data = $one; + if (empty($one[$this->alias])) { + if ($this->getAssociated(key($one)) === null) { + $data = array($this->alias => $one); + } + } + } else { + $data = array($this->alias => array($one => $two)); + } + + foreach ($data as $modelName => $fieldSet) { + if (is_array($fieldSet)) { + + foreach ($fieldSet as $fieldName => $fieldValue) { + if (isset($this->validationErrors[$fieldName])) { + unset ($this->validationErrors[$fieldName]); + } + + if ($modelName === $this->alias) { + if ($fieldName === $this->primaryKey) { + $this->id = $fieldValue; + } + } + if (is_array($fieldValue) || is_object($fieldValue)) { + $fieldValue = $this->deconstruct($fieldName, $fieldValue); + } + $this->data[$modelName][$fieldName] = $fieldValue; + } + } + } + return $data; + } + +/** + * Normalize Xml::toArray() to use in Model::save() + * + * @param array $xml XML as array + * @return array + */ + protected function _normalizeXmlData(array $xml) { + $return = array(); + foreach ($xml as $key => $value) { + if (is_array($value)) { + $return[Inflector::camelize($key)] = $this->_normalizeXmlData($value); + } elseif ($key[0] === '@') { + $return[substr($key, 1)] = $value; + } else { + $return[$key] = $value; + } + } + return $return; + } + +/** + * Deconstructs a complex data type (array or object) into a single field value. + * + * @param string $field The name of the field to be deconstructed + * @param mixed $data An array or object to be deconstructed into a field + * @return mixed The resulting data that should be assigned to a field + */ + public function deconstruct($field, $data) { + if (!is_array($data)) { + return $data; + } + + $copy = $data; + $type = $this->getColumnType($field); + + if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) { + $useNewDate = (isset($data['year']) || isset($data['month']) || + isset($data['day']) || isset($data['hour']) || isset($data['minute'])); + + $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec'); + $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec'); + $date = array(); + + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) { + $data['hour'] = $data['hour'] + 12; + } + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) { + $data['hour'] = '00'; + } + if ($type == 'time') { + foreach ($timeFields as $key => $val) { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } elseif ($data[$val] === '') { + $data[$val] = ''; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + if (!empty($data[$val])) { + $date[$key] = $data[$val]; + } else { + return null; + } + } + } + + if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') { + foreach ($dateFields as $key => $val) { + if ($val == 'hour' || $val == 'min' || $val == 'sec') { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + } + if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) { + return null; + } + if (isset($data[$val]) && !empty($data[$val])) { + $date[$key] = $data[$val]; + } + } + } + + $format = $this->getDataSource()->columns[$type]['format']; + $day = empty($date['Y']) ? null : $date['Y'] . '-' . $date['m'] . '-' . $date['d'] . ' '; + $hour = empty($date['H']) ? null : $date['H'] . ':' . $date['i'] . ':' . $date['s']; + $date = new DateTime($day . $hour); + if ($useNewDate && !empty($date)) { + return $date->format($format); + } + } + return $data; + } + +/** + * Returns an array of table metadata (column names and types) from the database. + * $field => keys(type, null, default, key, length, extra) + * + * @param mixed $field Set to true to reload schema, or a string to return a specific field + * @return array Array of table metadata + */ + public function schema($field = false) { + if (!is_array($this->_schema) || $field === true) { + $db = $this->getDataSource(); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + if (method_exists($db, 'describe') && $this->useTable !== false) { + $this->_schema = $db->describe($this); + } elseif ($this->useTable === false) { + $this->_schema = array(); + } + } + if (is_string($field)) { + if (isset($this->_schema[$field])) { + return $this->_schema[$field]; + } else { + return null; + } + } + return $this->_schema; + } + +/** + * Returns an associative array of field names and column types. + * + * @return array Field types indexed by field name + */ + public function getColumnTypes() { + $columns = $this->schema(); + if (empty($columns)) { + trigger_error(__d('cake_dev', '(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()'), E_USER_WARNING); + } + $cols = array(); + foreach ($columns as $field => $values) { + $cols[$field] = $values['type']; + } + return $cols; + } + +/** + * Returns the column type of a column in the model. + * + * @param string $column The name of the model column + * @return string Column type + */ + public function getColumnType($column) { + $db = $this->getDataSource(); + $cols = $this->schema(); + $model = null; + + $column = str_replace(array($db->startQuote, $db->endQuote), '', $column); + + if (strpos($column, '.')) { + list($model, $column) = explode('.', $column); + } + if ($model != $this->alias && isset($this->{$model})) { + return $this->{$model}->getColumnType($column); + } + if (isset($cols[$column]) && isset($cols[$column]['type'])) { + return $cols[$column]['type']; + } + return null; + } + +/** + * Returns true if the supplied field exists in the model's database table. + * + * @param mixed $name Name of field to look for, or an array of names + * @param boolean $checkVirtual checks if the field is declared as virtual + * @return mixed If $name is a string, returns a boolean indicating whether the field exists. + * If $name is an array of field names, returns the first field that exists, + * or false if none exist. + */ + public function hasField($name, $checkVirtual = false) { + if (is_array($name)) { + foreach ($name as $n) { + if ($this->hasField($n, $checkVirtual)) { + return $n; + } + } + return false; + } + + if ($checkVirtual && !empty($this->virtualFields)) { + if ($this->isVirtualField($name)) { + return true; + } + } + + if (empty($this->_schema)) { + $this->schema(); + } + + if ($this->_schema != null) { + return isset($this->_schema[$name]); + } + return false; + } + +/** + * Check that a method is callable on a model. This will check both the model's own methods, its + * inherited methods and methods that could be callable through behaviors. + * + * @param string $method The method to be called. + * @return boolean True on method being callable. + */ + public function hasMethod($method) { + if (method_exists($this, $method)) { + return true; + } + if ($this->Behaviors->hasMethod($method)) { + return true; + } + return false; + } + +/** + * Returns true if the supplied field is a model Virtual Field + * + * @param string $field Name of field to look for + * @return boolean indicating whether the field exists as a model virtual field. + */ + public function isVirtualField($field) { + if (empty($this->virtualFields) || !is_string($field)) { + return false; + } + if (isset($this->virtualFields[$field])) { + return true; + } + if (strpos($field, '.') !== false) { + list($model, $field) = explode('.', $field); + if (isset($this->virtualFields[$field])) { + return true; + } + } + return false; + } + +/** + * Returns the expression for a model virtual field + * + * @param string $field Name of field to look for + * @return mixed If $field is string expression bound to virtual field $field + * If $field is null, returns an array of all model virtual fields + * or false if none $field exist. + */ + public function getVirtualField($field = null) { + if ($field == null) { + return empty($this->virtualFields) ? false : $this->virtualFields; + } + if ($this->isVirtualField($field)) { + if (strpos($field, '.') !== false) { + list($model, $field) = explode('.', $field); + } + return $this->virtualFields[$field]; + } + return false; + } + +/** + * Initializes the model for writing a new record, loading the default values + * for those fields that are not defined in $data, and clearing previous validation errors. + * Especially helpful for saving data in loops. + * + * @param mixed $data Optional data array to assign to the model after it is created. If null or false, + * schema data defaults are not merged. + * @param boolean $filterKey If true, overwrites any primary key input with an empty value + * @return array The current Model::data; after merging $data and/or defaults from database + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function create($data = array(), $filterKey = false) { + $defaults = array(); + $this->id = false; + $this->data = array(); + $this->validationErrors = array(); + + if ($data !== null && $data !== false) { + foreach ($this->schema() as $field => $properties) { + if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') { + $defaults[$field] = $properties['default']; + } + } + $this->set($defaults); + $this->set($data); + } + if ($filterKey) { + $this->set($this->primaryKey, false); + } + return $this->data; + } + +/** + * Returns a list of fields from the database, and sets the current model + * data (Model::$data) with the record found. + * + * @param mixed $fields String of single fieldname, or an array of fieldnames. + * @param mixed $id The ID of the record to read + * @return array Array of database fields, or false if not found + * @link http://book.cakephp.org/view/1017/Retrieving-Your-Data#read-1029 + */ + public function read($fields = null, $id = null) { + $this->validationErrors = array(); + + if ($id != null) { + $this->id = $id; + } + + $id = $this->id; + + if (is_array($this->id)) { + $id = $this->id[0]; + } + + if ($id !== null && $id !== false) { + $this->data = $this->find('first', array( + 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), + 'fields' => $fields + )); + return $this->data; + } else { + return false; + } + } + +/** + * Returns the contents of a single field given the supplied conditions, in the + * supplied order. + * + * @param string $name Name of field to get + * @param array $conditions SQL conditions (defaults to NULL) + * @param string $order SQL ORDER BY fragment + * @return string field contents, or false if not found + * @link http://book.cakephp.org/view/1017/Retrieving-Your-Data#field-1028 + */ + public function field($name, $conditions = null, $order = null) { + if ($conditions === null && $this->id !== false) { + $conditions = array($this->alias . '.' . $this->primaryKey => $this->id); + } + if ($this->recursive >= 1) { + $recursive = -1; + } else { + $recursive = $this->recursive; + } + $fields = $name; + if ($data = $this->find('first', compact('conditions', 'fields', 'order', 'recursive'))) { + if (strpos($name, '.') === false) { + if (isset($data[$this->alias][$name])) { + return $data[$this->alias][$name]; + } + } else { + $name = explode('.', $name); + if (isset($data[$name[0]][$name[1]])) { + return $data[$name[0]][$name[1]]; + } + } + if (isset($data[0]) && count($data[0]) > 0) { + return array_shift($data[0]); + } + } else { + return false; + } + } + +/** + * Saves the value of a single field to the database, based on the current + * model ID. + * + * @param string $name Name of the table field + * @param mixed $value Value of the field + * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed + * @return boolean See Model::save() + * @see Model::save() + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function saveField($name, $value, $validate = false) { + $id = $this->id; + $this->create(false); + + if (is_array($validate)) { + $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate); + } else { + $options = array('validate' => $validate, 'fieldList' => array($name)); + } + return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options); + } + +/** + * Saves model data (based on white-list, if supplied) to the database. By + * default, validation occurs before save. + * + * @param array $data Data to save. + * @param mixed $validate Either a boolean, or an array. + * If a boolean, indicates whether or not to validate before saving. + * If an array, allows control of validate, callbacks, and fieldList + * @param array $fieldList List of fields to allow to be written + * @return mixed On success Model::$data if its not empty or true, false on failure + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function save($data = null, $validate = true, $fieldList = array()) { + $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true); + $_whitelist = $this->whitelist; + $fields = array(); + + if (!is_array($validate)) { + $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks')); + } else { + $options = array_merge($defaults, $validate); + } + + if (!empty($options['fieldList'])) { + $this->whitelist = $options['fieldList']; + } elseif ($options['fieldList'] === null) { + $this->whitelist = array(); + } + $this->set($data); + + if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) { + return false; + } + + foreach (array('created', 'updated', 'modified') as $field) { + $keyPresentAndEmpty = ( + isset($this->data[$this->alias]) && + array_key_exists($field, $this->data[$this->alias]) && + $this->data[$this->alias][$field] === null + ); + if ($keyPresentAndEmpty) { + unset($this->data[$this->alias][$field]); + } + } + + $exists = $this->exists(); + $dateFields = array('modified', 'updated'); + + if (!$exists) { + $dateFields[] = 'created'; + } + if (isset($this->data[$this->alias])) { + $fields = array_keys($this->data[$this->alias]); + } + if ($options['validate'] && !$this->validates($options)) { + $this->whitelist = $_whitelist; + return false; + } + + $db = $this->getDataSource(); + + foreach ($dateFields as $updateCol) { + if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) { + $default = array('formatter' => 'date'); + $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]); + if (!array_key_exists('format', $colType)) { + $time = strtotime('now'); + } else { + $time = $colType['formatter']($colType['format']); + } + if (!empty($this->whitelist)) { + $this->whitelist[] = $updateCol; + } + $this->set($updateCol, $time); + } + } + + if ($options['callbacks'] === true || $options['callbacks'] === 'before') { + $result = $this->Behaviors->trigger('beforeSave', array(&$this, $options), array( + 'break' => true, 'breakOn' => array(false, null) + )); + if (!$result || !$this->beforeSave($options)) { + $this->whitelist = $_whitelist; + return false; + } + } + + if (empty($this->data[$this->alias][$this->primaryKey])) { + unset($this->data[$this->alias][$this->primaryKey]); + } + $fields = $values = array(); + + foreach ($this->data as $n => $v) { + if (isset($this->hasAndBelongsToMany[$n])) { + if (isset($v[$n])) { + $v = $v[$n]; + } + $joined[$n] = $v; + } else { + if ($n === $this->alias) { + foreach (array('created', 'updated', 'modified') as $field) { + if (array_key_exists($field, $v) && empty($v[$field])) { + unset($v[$field]); + } + } + + foreach ($v as $x => $y) { + if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) { + list($fields[], $values[]) = array($x, $y); + } + } + } + } + } + $count = count($fields); + + if (!$exists && $count > 0) { + $this->id = false; + } + $success = true; + $created = false; + + if ($count > 0) { + $cache = $this->_prepareUpdateFields(array_combine($fields, $values)); + + if (!empty($this->id)) { + $success = (bool)$db->update($this, $fields, $values); + } else { + $fInfo = $this->schema($this->primaryKey); + $isUUID = ($fInfo['length'] == 36 && + ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary') + ); + if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) { + if (array_key_exists($this->primaryKey, $this->data[$this->alias])) { + $j = array_search($this->primaryKey, $fields); + $values[$j] = String::uuid(); + } else { + list($fields[], $values[]) = array($this->primaryKey, String::uuid()); + } + } + + if (!$db->create($this, $fields, $values)) { + $success = $created = false; + } else { + $created = true; + } + } + + if ($success && !empty($this->belongsTo)) { + $this->updateCounterCache($cache, $created); + } + } + + if (!empty($joined) && $success === true) { + $this->_saveMulti($joined, $this->id, $db); + } + + if ($success && $count > 0) { + if (!empty($this->data)) { + $success = $this->data; + if ($created) { + $this->data[$this->alias][$this->primaryKey] = $this->id; + } + } + if ($options['callbacks'] === true || $options['callbacks'] === 'after') { + $this->Behaviors->trigger('afterSave', array(&$this, $created, $options)); + $this->afterSave($created); + } + if (!empty($this->data)) { + $success = Set::merge($success, $this->data); + } + $this->data = false; + $this->_clearCache(); + $this->validationErrors = array(); + } + $this->whitelist = $_whitelist; + return $success; + } + +/** + * Saves model hasAndBelongsToMany data to the database. + * + * @param array $joined Data to save + * @param mixed $id ID of record in this model + * @param DataSource $db + * @return void + */ + protected function _saveMulti($joined, $id, $db) { + foreach ($joined as $assoc => $data) { + + if (isset($this->hasAndBelongsToMany[$assoc])) { + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + + $keyInfo = $this->{$join}->schema($this->{$join}->primaryKey); + $isUUID = !empty($this->{$join}->primaryKey) && ( + $keyInfo['length'] == 36 && ( + $keyInfo['type'] === 'string' || + $keyInfo['type'] === 'binary' + ) + ); + + $newData = $newValues = array(); + $primaryAdded = false; + + $fields = array( + $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']), + $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']) + ); + + $idField = $db->name($this->{$join}->primaryKey); + if ($isUUID && !in_array($idField, $fields)) { + $fields[] = $idField; + $primaryAdded = true; + } + + foreach ((array)$data as $row) { + if (!empty($row) && (is_string($row) /*&& (strlen($row) == 36 || strlen($row) == 16)*/) || is_numeric($row)) { # QUICKAPPS MOD + $values = array($id, $row); + if ($isUUID && $primaryAdded) { + $values[] = String::uuid(); + } + $newValues[] = $values; + unset($values); + } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + + if ($this->hasAndBelongsToMany[$assoc]['unique']) { + $conditions = array( + $join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id + ); + if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) { + $conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']); + } + $links = $this->{$join}->find('all', array( + 'conditions' => $conditions, + 'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0, + 'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey'] + )); + + $associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey']; + $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}"); + if (!empty($oldLinks)) { + $conditions[$associationForeignKey] = $oldLinks; + $db->delete($this->{$join}, $conditions); + } + } + + if (!empty($newData)) { + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id; + $this->{$join}->create($data); + $this->{$join}->save(); + } + } + + if (!empty($newValues)) { + $db->insertMulti($this->{$join}, $fields, $newValues); + } + } + } + } + +/** + * Updates the counter cache of belongsTo associations after a save or delete operation + * + * @param array $keys Optional foreign key data, defaults to the information $this->data + * @param boolean $created True if a new record was created, otherwise only associations with + * 'counterScope' defined get updated + * @return void + */ + public function updateCounterCache($keys = array(), $created = false) { + $keys = empty($keys) ? $this->data[$this->alias] : $keys; + $keys['old'] = isset($keys['old']) ? $keys['old'] : array(); + + foreach ($this->belongsTo as $parent => $assoc) { + if (!empty($assoc['counterCache'])) { + if (!is_array($assoc['counterCache'])) { + if (isset($assoc['counterScope'])) { + $assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']); + } else { + $assoc['counterCache'] = array($assoc['counterCache'] => array()); + } + } + + $foreignKey = $assoc['foreignKey']; + $fkQuoted = $this->escapeField($assoc['foreignKey']); + + foreach ($assoc['counterCache'] as $field => $conditions) { + if (!is_string($field)) { + $field = Inflector::underscore($this->alias) . '_count'; + } + if (!$this->{$parent}->hasField($field)) { + continue; + } + if ($conditions === true) { + $conditions = array(); + } else { + $conditions = (array)$conditions; + } + + if (!array_key_exists($foreignKey, $keys)) { + $keys[$foreignKey] = $this->field($foreignKey); + } + $recursive = (empty($conditions) ? -1 : 0); + + if (isset($keys['old'][$foreignKey])) { + if ($keys['old'][$foreignKey] != $keys[$foreignKey]) { + $conditions[$fkQuoted] = $keys['old'][$foreignKey]; + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($field => $count), + array($this->{$parent}->escapeField() => $keys['old'][$foreignKey]) + ); + } + } + $conditions[$fkQuoted] = $keys[$foreignKey]; + + if ($recursive === 0) { + $conditions = array_merge($conditions, (array)$conditions); + } + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($field => $count), + array($this->{$parent}->escapeField() => $keys[$foreignKey]) + ); + } + } + } + } + +/** + * Helper method for Model::updateCounterCache(). Checks the fields to be updated for + * + * @param array $data The fields of the record that will be updated + * @return array Returns updated foreign key values, along with an 'old' key containing the old + * values, or empty if no foreign keys are updated. + */ + protected function _prepareUpdateFields($data) { + $foreignKeys = array(); + foreach ($this->belongsTo as $assoc => $info) { + if ($info['counterCache']) { + $foreignKeys[$assoc] = $info['foreignKey']; + } + } + $included = array_intersect($foreignKeys, array_keys($data)); + + if (empty($included) || empty($this->id)) { + return array(); + } + $old = $this->find('first', array( + 'conditions' => array($this->primaryKey => $this->id), + 'fields' => array_values($included), + 'recursive' => -1 + )); + return array_merge($data, array('old' => $old[$this->alias])); + } + +/** + * Backwards compatible passtrough method for: + * saveMany(), validateMany(), saveAssociated() and validateAssociated() + * + * Saves multiple individual records for a single model; Also works with a single record, as well as + * all its associated records. + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * or 'only' to only validate the records, but not save them. + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple + * records of the same type), or an array indexed by association name. + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + * @link http://book.cakephp.org/view/1032/Saving-Related-Model-Data-hasOne-hasMany-belongsTo + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function saveAll($data = null, $options = array()) { + $options = array_merge(array('validate' => 'first'), $options); + if (Set::numeric(array_keys($data))) { + if ($options['validate'] === 'only') { + return $this->validateMany($data, $options); + } + return $this->saveMany($data, $options); + } + if ($options['validate'] === 'only') { + $validatesAssoc = $this->validateAssociated($data, $options); + if (isset($this->validationErrors[$this->alias]) && $this->validationErrors[$this->alias] === false) { + return false; + } + return $validatesAssoc; + } + return $this->saveAssociated($data, $options); + } + +/** + * Saves multiple individual records for a single model + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This should be a numerically-indexed array + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + */ + public function saveMany($data = null, $options = array()) { + if (empty($data)) { + $data = $this->data; + } + + $options = array_merge(array('validate' => 'first', 'atomic' => true), $options); + $this->validationErrors = $validationErrors = array(); + + if (empty($data) && $options['validate'] !== false) { + $result = $this->save($data, $options); + return !empty($result); + } + + if ($options['validate'] === 'first') { + $validates = $this->validateMany($data, $options); + if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { + return $validates; + } + } + + if ($options['atomic']) { + $db = $this->getDataSource(); + $transactionBegun = $db->begin($this); + } + $return = array(); + foreach ($data as $key => $record) { + $validates = ($this->create(null) !== null && $this->save($record, $options)); + if (!$validates) { + $validationErrors[$key] = $this->validationErrors; + } + if (!$options['atomic']) { + $return[] = $validates; + } elseif (!$validates) { + break; + } + } + $this->validationErrors = $validationErrors; + + if (!$options['atomic']) { + return $return; + } + if ($validates) { + if ($transactionBegun) { + return $db->commit($this) !== false; + } else { + return true; + } + } + $db->rollback($this); + return false; + } + +/** + * Validates multiple individual records for a single model + * + * #### Options + * + * - atomic: If true (default), returns boolean. If false returns array. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to validate. This should be a numerically-indexed array + * @param array $options Options to use when validating record data (see above), See also $options of validates(). + * @return boolean True on success, or false on failure. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record validated successfully. + */ + public function validateMany($data, $options = array()) { + $options = array_merge(array('atomic' => true), $options); + $this->validationErrors = $validationErrors = $return = array(); + foreach ($data as $key => $record) { + $validates = $this->create($record) && $this->validates($options); + if (!$validates) { + $validationErrors[$key] = $this->validationErrors; + } + $return[] = $validates; + } + $this->validationErrors = $validationErrors; + if (!$options['atomic']) { + return $return; + } + if (empty($this->validationErrors)) { + return true; + } + return false; + } + +/** + * Saves a single record, as well as all its directly associated records. + * + * #### Options + * + * - validate: Set to false to disable validation, true to validate each record before saving, + * 'first' to validate *all* records before any are saved (default), + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to save. This should be an array indexed by association name. + * @param array $options Options to use when saving record data, See $options above. + * @return mixed If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + */ + public function saveAssociated($data = null, $options = array()) { + if (empty($data)) { + $data = $this->data; + } + + $options = array_merge(array('validate' => true, 'atomic' => true), $options); + $this->validationErrors = $validationErrors = array(); + + if (empty($data) && $options['validate'] !== false) { + $result = $this->save($data, $options); + return !empty($result); + } + + if ($options['validate'] === 'first') { + $validates = $this->validateAssociated($data, $options); + if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { + return $validates; + } + } + if ($options['atomic']) { + $db = $this->getDataSource(); + $transactionBegun = $db->begin($this); + } + $associations = $this->getAssociated(); + $return = array(); + $validates = true; + foreach ($data as $association => $values) { + if (isset($associations[$association]) && $associations[$association] === 'belongsTo') { + if ($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options)) { + $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id; + } else { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association][] = $validates; + } + } + if ($validates && !($this->create(null) !== null && $this->save($data, $options))) { + $validationErrors[$this->alias] = $this->validationErrors; + $validates = false; + } + $return[$this->alias] = $validates; + + foreach ($data as $association => $values) { + if (!$validates) { + break; + } + if (isset($associations[$association])) { + $type = $associations[$association]; + switch ($type) { + case 'hasOne': + $values[$this->{$type}[$association]['foreignKey']] = $this->id; + if (!($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options))) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association][] = $validates; + break; + case 'hasMany': + foreach ($values as $i => $value) { + $values[$i][$this->{$type}[$association]['foreignKey']] = $this->id; + } + $_return = $this->{$association}->saveMany($values, array_merge($options, array('atomic' => false))); + if (in_array(false, $_return, true)) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + $return[$association] = $_return; + break; + } + } + } + $this->validationErrors = $validationErrors; + + if (isset($validationErrors[$this->alias])) { + $this->validationErrors = $validationErrors[$this->alias]; + } + + if (!$options['atomic']) { + return $return; + } + if ($validates) { + if ($transactionBegun) { + return $db->commit($this) !== false; + } else { + return true; + } + } + $db->rollback($this); + return false; + } + +/** + * Validates a single record, as well as all its directly associated records. + * + * #### Options + * + * - atomic: If true (default), returns boolean. If false returns array. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * + * @param array $data Record data to validate. This should be an array indexed by association name. + * @param array $options Options to use when validating record data (see above), See also $options of validates(). + * @return array|boolean If atomic: True on success, or false on failure. + * Otherwise: array similar to the $data array passed, but values are set to true/false + * depending on whether each record validated successfully. + */ + public function validateAssociated($data, $options = array()) { + $options = array_merge(array('atomic' => true), $options); + $this->validationErrors = $validationErrors = $return = array(); + if (!($this->create($data) && $this->validates($options))) { + $validationErrors[$this->alias] = $this->validationErrors; + $return[$this->alias] = false; + } else { + $return[$this->alias] = true; + } + $associations = $this->getAssociated(); + $validates = true; + foreach ($data as $association => $values) { + if (isset($associations[$association])) { + if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { + $validates = $this->{$association}->create($values) && $this->{$association}->validates($options); + $return[$association][] = $validates; + } elseif($associations[$association] === 'hasMany') { + $validates = $this->{$association}->validateMany($values, $options); + $return[$association] = $validates; + } + if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { + $validationErrors[$association] = $this->{$association}->validationErrors; + } + } + } + + $this->validationErrors = $validationErrors; + if (isset($validationErrors[$this->alias])) { + $this->validationErrors = $validationErrors[$this->alias]; + } + if (!$options['atomic']) { + return $return; + } + if ($return[$this->alias] === false || !empty($this->validationErrors)) { + return false; + } + return true; + } + +/** + * Updates multiple model records based on a set of conditions. + * + * @param array $fields Set of fields and values, indexed by fields. + * Fields are treated as SQL snippets, to insert literal values manually escape your data. + * @param mixed $conditions Conditions to match, true for all records + * @return boolean True on success, false on failure + * @link http://book.cakephp.org/view/1031/Saving-Your-Data + */ + public function updateAll($fields, $conditions = true) { + return $this->getDataSource()->update($this, $fields, null, $conditions); + } + +/** + * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success. + * + * @param mixed $id ID of record to delete + * @param boolean $cascade Set to true to delete records that depend on this record + * @return boolean True on success + * @link http://book.cakephp.org/view/1036/delete + */ + public function delete($id = null, $cascade = true) { + if (!empty($id)) { + $this->id = $id; + } + $id = $this->id; + + if ($this->beforeDelete($cascade)) { + $filters = $this->Behaviors->trigger( + 'beforeDelete', + array(&$this, $cascade), + array('break' => true, 'breakOn' => array(false, null)) + ); + if (!$filters || !$this->exists()) { + return false; + } + $db = $this->getDataSource(); + + $this->_deleteDependent($id, $cascade); + $this->_deleteLinks($id); + $this->id = $id; + + $updateCounterCache = false; + if (!empty($this->belongsTo)) { + foreach ($this->belongsTo as $parent => $assoc) { + if (!empty($assoc['counterCache'])) { + $updateCounterCache = true; + break; + } + } + + $keys = $this->find('first', array( + 'fields' => $this->_collectForeignKeys(), + 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), + 'recursive' => -1, + 'callbacks' => false + )); + } + + if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) { + if ($updateCounterCache) { + $this->updateCounterCache($keys[$this->alias]); + } + $this->Behaviors->trigger('afterDelete', array(&$this)); + $this->afterDelete(); + $this->_clearCache(); + $this->id = false; + return true; + } + } + return false; + } + +/** + * Cascades model deletes through associated hasMany and hasOne child records. + * + * @param string $id ID of record that was deleted + * @param boolean $cascade Set to true to delete records that depend on this record + * @return void + */ + protected function _deleteDependent($id, $cascade) { + if (!empty($this->__backAssociation)) { + $savedAssociatons = $this->__backAssociation; + $this->__backAssociation = array(); + } + foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) { + if ($data['dependent'] === true && $cascade === true) { + + $model = $this->{$assoc}; + $conditions = array($model->escapeField($data['foreignKey']) => $id); + if ($data['conditions']) { + $conditions = array_merge((array)$data['conditions'], $conditions); + } + $model->recursive = -1; + + if (isset($data['exclusive']) && $data['exclusive']) { + $model->deleteAll($conditions); + } else { + $records = $model->find('all', array( + 'conditions' => $conditions, 'fields' => $model->primaryKey + )); + + if (!empty($records)) { + foreach ($records as $record) { + $model->delete($record[$model->alias][$model->primaryKey]); + } + } + } + } + } + if (isset($savedAssociatons)) { + $this->__backAssociation = $savedAssociatons; + } + } + +/** + * Cascades model deletes through HABTM join keys. + * + * @param string $id ID of record that was deleted + * @return void + */ + protected function _deleteLinks($id) { + foreach ($this->hasAndBelongsToMany as $assoc => $data) { + list($plugin, $joinModel) = pluginSplit($data['with']); + $records = $this->{$joinModel}->find('all', array( + 'conditions' => array_merge(array($this->{$joinModel}->escapeField($data['foreignKey']) => $id)), + 'fields' => $this->{$joinModel}->primaryKey, + 'recursive' => -1, + 'callbacks' => false + )); + if (!empty($records)) { + foreach ($records as $record) { + $this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->primaryKey]); + } + } + } + } + +/** + * Deletes multiple model records based on a set of conditions. + * + * @param mixed $conditions Conditions to match + * @param boolean $cascade Set to true to delete records that depend on this record + * @param boolean $callbacks Run callbacks + * @return boolean True on success, false on failure + * @link http://book.cakephp.org/view/1038/deleteAll + */ + public function deleteAll($conditions, $cascade = true, $callbacks = false) { + if (empty($conditions)) { + return false; + } + $db = $this->getDataSource(); + + if (!$cascade && !$callbacks) { + return $db->delete($this, $conditions); + } else { + $ids = $this->find('all', array_merge(array( + 'fields' => "{$this->alias}.{$this->primaryKey}", + 'recursive' => 0), compact('conditions')) + ); + if ($ids === false) { + return false; + } + + $ids = Set::extract($ids, "{n}.{$this->alias}.{$this->primaryKey}"); + if (empty($ids)) { + return true; + } + + if ($callbacks) { + $_id = $this->id; + $result = true; + foreach ($ids as $id) { + $result = ($result && $this->delete($id, $cascade)); + } + $this->id = $_id; + return $result; + } else { + foreach ($ids as $id) { + $this->_deleteLinks($id); + if ($cascade) { + $this->_deleteDependent($id, $cascade); + } + } + return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids)); + } + } + } + +/** + * Collects foreign keys from associations. + * + * @param string $type + * @return array + */ + protected function _collectForeignKeys($type = 'belongsTo') { + $result = array(); + + foreach ($this->{$type} as $assoc => $data) { + if (isset($data['foreignKey']) && is_string($data['foreignKey'])) { + $result[$assoc] = $data['foreignKey']; + } + } + return $result; + } + +/** + * Returns true if a record with the currently set ID exists. + * + * Internally calls Model::getID() to obtain the current record ID to verify, + * and then performs a Model::find('count') on the currently configured datasource + * to ascertain the existence of the record in persistent storage. + * + * @return boolean True if such a record exists + */ + public function exists() { + if ($this->getID() === false) { + return false; + } + $conditions = array($this->alias . '.' . $this->primaryKey => $this->getID()); + $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false); + return ($this->find('count', $query) > 0); + } + +/** + * Returns true if a record that meets given conditions exists. + * + * @param array $conditions SQL conditions array + * @return boolean True if such a record exists + */ + public function hasAny($conditions = null) { + return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false); + } + +/** + * Queries the datasource and returns a result set array. + * + * Also used to perform notation finds, where the first argument is type of find operation to perform + * (all / first / count / neighbors / list / threaded ), + * second parameter options for finding ( indexed array, including: 'conditions', 'limit', + * 'recursive', 'page', 'fields', 'offset', 'order') + * + * Eg: + * {{{ + * find('all', array( + * 'conditions' => array('name' => 'Thomas Anderson'), + * 'fields' => array('name', 'email'), + * 'order' => 'field3 DESC', + * 'recursive' => 2, + * 'group' => 'type' + * )); + * }}} + * + * In addition to the standard query keys above, you can provide Datasource, and behavior specific + * keys. For example, when using a SQL based datasource you can use the joins key to specify additional + * joins that should be part of the query. + * + * {{{ + * find('all', array( + * 'conditions' => array('name' => 'Thomas Anderson'), + * 'joins' => array( + * array( + * 'alias' => 'Thought', + * 'table' => 'thoughts', + * 'type' => 'LEFT', + * 'conditions' => '`Thought`.`person_id` = `Person`.`id`' + * ) + * ) + * )); + * }}} + * + * Behaviors and find types can also define custom finder keys which are passed into find(). + * + * Specifying 'fields' for notation 'list': + * + * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value. + * - If a single field is specified, 'id' is used for key and specified field is used for value. + * - If three fields are specified, they are used (in order) for key, value and group. + * - Otherwise, first and second fields are used for key and value. + * + * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) + * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) + * @return array Array of records + * @link http://book.cakephp.org/view/1018/find + */ + public function find($type = 'first', $query = array()) { + $this->findQueryType = $type; + $this->id = $this->getID(); + + $query = $this->buildQuery($type, $query); + if (is_null($query)) { + return null; + } + + $results = $this->getDataSource()->read($this, $query); + $this->resetAssociations(); + + if ($query['callbacks'] === true || $query['callbacks'] === 'after') { + $results = $this->_filterResults($results); + } + + $this->findQueryType = null; + + if ($type === 'all') { + return $results; + } else { + if ($this->findMethods[$type] === true) { + return $this->{'_find' . ucfirst($type)}('after', $query, $results); + } + } + } + +/** + * Builds the query array that is used by the data source to generate the query to fetch the data. + * + * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) + * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) + * @return array Query array or null if it could not be build for some reasons + * @see Model::find() + */ + public function buildQuery($type = 'first', $query = array()) { + $query = array_merge( + array( + 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null, + 'offset' => null, 'order' => null, 'page' => 1, 'group' => null, 'callbacks' => true, + ), + (array)$query + ); + + if ($type !== 'all') { + if ($this->findMethods[$type] === true) { + $query = $this->{'_find' . ucfirst($type)}('before', $query); + } + } + + if (!is_numeric($query['page']) || intval($query['page']) < 1) { + $query['page'] = 1; + } + if ($query['page'] > 1 && !empty($query['limit'])) { + $query['offset'] = ($query['page'] - 1) * $query['limit']; + } + if ($query['order'] === null && $this->order !== null) { + $query['order'] = $this->order; + } + $query['order'] = array($query['order']); + + if ($query['callbacks'] === true || $query['callbacks'] === 'before') { + $return = $this->Behaviors->trigger( + 'beforeFind', + array(&$this, $query), + array('break' => true, 'breakOn' => array(false, null), 'modParams' => 1) + ); + + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + + $return = $this->beforeFind($query); + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + } + + return $query; + } + +/** + * Handles the before/after filter logic for find('first') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return array + * @see Model::find() + */ + protected function _findFirst($state, $query, $results = array()) { + if ($state === 'before') { + $query['limit'] = 1; + return $query; + } elseif ($state === 'after') { + if (empty($results[0])) { + return false; + } + return $results[0]; + } + } + +/** + * Handles the before/after filter logic for find('count') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return integer The number of records found, or false + * @see Model::find() + */ + protected function _findCount($state, $query, $results = array()) { + if ($state === 'before') { + $db = $this->getDataSource(); + if (empty($query['fields'])) { + $query['fields'] = $db->calculate($this, 'count'); + } elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) { + $query['fields'] = $db->calculate($this, 'count', array( + $db->expression($query['fields']), 'count' + )); + } + $query['order'] = false; + return $query; + } elseif ($state === 'after') { + foreach (array(0, $this->alias) as $key) { + if (isset($results[0][$key]['count'])) { + if (count($results) > 1) { + return intval(array_sum(Set::extract('/' . $key . '/count', $results))); + } else { + return intval($results[0][$key]['count']); + } + } + } + return false; + } + } + +/** + * Handles the before/after filter logic for find('list') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $results + * @return array Key/value pairs of primary keys/display field values of all records found + * @see Model::find() + */ + protected function _findList($state, $query, $results = array()) { + if ($state === 'before') { + if (empty($query['fields'])) { + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}"); + $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null); + } else { + if (!is_array($query['fields'])) { + $query['fields'] = String::tokenize($query['fields']); + } + + if (count($query['fields']) === 1) { + if (strpos($query['fields'][0], '.') === false) { + $query['fields'][0] = $this->alias . '.' . $query['fields'][0]; + } + + $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null); + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]); + } elseif (count($query['fields']) === 3) { + for ($i = 0; $i < 3; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]); + } else { + for ($i = 0; $i < 2; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null); + } + } + if (!isset($query['recursive']) || $query['recursive'] === null) { + $query['recursive'] = -1; + } + list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list; + return $query; + } elseif ($state === 'after') { + if (empty($results)) { + return array(); + } + $lst = $query['list']; + return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']); + } + } + +/** + * Detects the previous field's value, then uses logic to find the 'wrapping' + * rows and return them. + * + * @param string $state Either "before" or "after" + * @param mixed $query + * @param array $results + * @return array + */ + protected function _findNeighbors($state, $query, $results = array()) { + if ($state === 'before') { + extract($query); + $conditions = (array)$conditions; + if (isset($field) && isset($value)) { + if (strpos($field, '.') === false) { + $field = $this->alias . '.' . $field; + } + } else { + $field = $this->alias . '.' . $this->primaryKey; + $value = $this->id; + } + $query['conditions'] = array_merge($conditions, array($field . ' <' => $value)); + $query['order'] = $field . ' DESC'; + $query['limit'] = 1; + $query['field'] = $field; + $query['value'] = $value; + return $query; + } elseif ($state === 'after') { + extract($query); + unset($query['conditions'][$field . ' <']); + $return = array(); + if (isset($results[0])) { + $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]); + $query['conditions'][$field . ' >='] = $prevVal[0]; + $query['conditions'][$field . ' !='] = $value; + $query['limit'] = 2; + } else { + $return['prev'] = null; + $query['conditions'][$field . ' >'] = $value; + $query['limit'] = 1; + } + $query['order'] = $field . ' ASC'; + $return2 = $this->find('all', $query); + if (!array_key_exists('prev', $return)) { + $return['prev'] = $return2[0]; + } + if (count($return2) === 2) { + $return['next'] = $return2[1]; + } elseif (count($return2) === 1 && !$return['prev']) { + $return['next'] = $return2[0]; + } else { + $return['next'] = null; + } + return $return; + } + } + +/** + * In the event of ambiguous results returned (multiple top level results, with different parent_ids) + * top level results with different parent_ids to the first result will be dropped + * + * @param mixed $state + * @param mixed $query + * @param array $results + * @return array Threaded results + */ + protected function _findThreaded($state, $query, $results = array()) { + if ($state === 'before') { + return $query; + } elseif ($state === 'after') { + $return = $idMap = array(); + $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey); + + foreach ($results as $result) { + $result['children'] = array(); + $id = $result[$this->alias][$this->primaryKey]; + $parentId = $result[$this->alias]['parent_id']; + if (isset($idMap[$id]['children'])) { + $idMap[$id] = array_merge($result, (array)$idMap[$id]); + } else { + $idMap[$id] = array_merge($result, array('children' => array())); + } + if (!$parentId || !in_array($parentId, $ids)) { + $return[] =& $idMap[$id]; + } else { + $idMap[$parentId]['children'][] =& $idMap[$id]; + } + } + if (count($return) > 1) { + $ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return)); + if (count($ids) > 1) { + $root = $return[0][$this->alias]['parent_id']; + foreach ($return as $key => $value) { + if ($value[$this->alias]['parent_id'] != $root) { + unset($return[$key]); + } + } + } + } + return $return; + } + } + +/** + * Passes query results through model and behavior afterFilter() methods. + * + * @param array $results Results to filter + * @param boolean $primary If this is the primary model results (results from model where the find operation was performed) + * @return array Set of filtered results + */ + protected function _filterResults($results, $primary = true) { + $return = $this->Behaviors->trigger( + 'afterFind', + array(&$this, $results, $primary), + array('modParams' => 1) + ); + if ($return !== true) { + $results = $return; + } + return $this->afterFind($results, $primary); + } + +/** + * This resets the association arrays for the model back + * to those originally defined in the model. Normally called at the end + * of each call to Model::find() + * + * @return boolean Success + */ + public function resetAssociations() { + if (!empty($this->__backAssociation)) { + foreach ($this->_associations as $type) { + if (isset($this->__backAssociation[$type])) { + $this->{$type} = $this->__backAssociation[$type]; + } + } + $this->__backAssociation = array(); + } + + foreach ($this->_associations as $type) { + foreach ($this->{$type} as $key => $name) { + if (property_exists($this, $key) && !empty($this->{$key}->__backAssociation)) { + $this->{$key}->resetAssociations(); + } + } + } + $this->__backAssociation = array(); + return true; + } + +/** + * Returns false if any fields passed match any (by default, all if $or = false) of their matching values. + * + * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data) + * @param boolean $or If false, all fields specified must match in order for a false return value + * @return boolean False if any records matching any fields are found + */ + public function isUnique($fields, $or = true) { + if (!is_array($fields)) { + $fields = func_get_args(); + if (is_bool($fields[count($fields) - 1])) { + $or = $fields[count($fields) - 1]; + unset($fields[count($fields) - 1]); + } + } + + foreach ($fields as $field => $value) { + if (is_numeric($field)) { + unset($fields[$field]); + + $field = $value; + if (isset($this->data[$this->alias][$field])) { + $value = $this->data[$this->alias][$field]; + } else { + $value = null; + } + } + + if (strpos($field, '.') === false) { + unset($fields[$field]); + $fields[$this->alias . '.' . $field] = $value; + } + } + if ($or) { + $fields = array('or' => $fields); + } + if (!empty($this->id)) { + $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id; + } + return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0); + } + +/** + * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method. + * + * @param string $sql,... SQL statement + * @return array Resultset + * @link http://book.cakephp.org/view/1027/query + */ + public function query($sql) { + $params = func_get_args(); + $db = $this->getDataSource(); + return call_user_func_array(array(&$db, 'query'), $params); + } + +/** + * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations + * that use the 'with' key as well. Since _saveMulti is incapable of exiting a save operation. + * + * Will validate the currently set data. Use Model::set() or Model::create() to set the active data. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return boolean True if there are no errors + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public function validates($options = array()) { + $errors = $this->invalidFields($options); + if (empty($errors) && $errors !== false) { + $errors = $this->_validateWithModels($options); + } + if (is_array($errors)) { + return count($errors) === 0; + } + return $errors; + } + +/** + * Returns an array of fields that have failed validation. On the current model. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return array Array of invalid fields + * @see Model::validates() + * @link http://book.cakephp.org/view/1182/Validating-Data-from-the-Controller + */ + public function invalidFields($options = array()) { + if ( + !$this->Behaviors->trigger( + 'beforeValidate', + array(&$this, $options), + array('break' => true, 'breakOn' => false) + ) || + $this->beforeValidate($options) === false + ) { + return false; + } + + if (!isset($this->validate) || empty($this->validate)) { + return $this->validationErrors; + } + + $data = $this->data; + $methods = array_map('strtolower', get_class_methods($this)); + $behaviorMethods = array_keys($this->Behaviors->methods()); + + if (isset($data[$this->alias])) { + $data = $data[$this->alias]; + } elseif (!is_array($data)) { + $data = array(); + } + + $exists = $this->exists(); + + $_validate = $this->validate; + $whitelist = $this->whitelist; + + if (!empty($options['fieldList'])) { + $whitelist = $options['fieldList']; + } + + if (!empty($whitelist)) { + $validate = array(); + foreach ((array)$whitelist as $f) { + if (!empty($this->validate[$f])) { + $validate[$f] = $this->validate[$f]; + } + } + $this->validate = $validate; + } + + foreach ($this->validate as $fieldName => $ruleSet) { + if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { + $ruleSet = array($ruleSet); + } + $default = array( + 'allowEmpty' => null, + 'required' => null, + 'rule' => 'blank', + 'last' => true, + 'on' => null + ); + + foreach ($ruleSet as $index => $validator) { + if (!is_array($validator)) { + $validator = array('rule' => $validator); + } + $validator = array_merge($default, $validator); + + $validationDomain = $this->validationDomain; + if (empty($validationDomain)) { + $validationDomain = 'default'; + } + if (isset($validator['message'])) { + $message = $validator['message']; + } else { + $message = __d('cake_dev', 'This field cannot be left blank'); + } + + if ( + empty($validator['on']) || ($validator['on'] == 'create' && + !$exists) || ($validator['on'] == 'update' && $exists + )) { + $required = ( + (!isset($data[$fieldName]) && $validator['required'] === true) || + ( + isset($data[$fieldName]) && (empty($data[$fieldName]) && + !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false + ) + ); + + if ($required) { + $this->invalidate($fieldName, __d($validationDomain, $message)); + if ($validator['last']) { + break; + } + } elseif (array_key_exists($fieldName, $data)) { + if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) { + break; + } + if (is_array($validator['rule'])) { + $rule = $validator['rule'][0]; + unset($validator['rule'][0]); + $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule'])); + } else { + $rule = $validator['rule']; + $ruleParams = array($data[$fieldName]); + } + + $valid = true; + + if (in_array(strtolower($rule), $methods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->dispatchMethod($rule, $ruleParams); + } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); + } elseif (method_exists('Validation', $rule)) { + $valid = call_user_func_array(array('Validation', $rule), $ruleParams); + } elseif (!is_array($validator['rule'])) { + $valid = preg_match($rule, $data[$fieldName]); + } elseif (Configure::read('debug') > 0) { + trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING); + } + + if (!$valid || (is_string($valid) && strlen($valid) > 0)) { + if (is_string($valid) && strlen($valid) > 0) { + $validator['message'] = $valid; + } elseif (!isset($validator['message'])) { + if (is_string($index)) { + $validator['message'] = $index; + } elseif (is_numeric($index) && count($ruleSet) > 1) { + $validator['message'] = $index + 1; + } else { + $validator['message'] = __d($validationDomain, $message); + } + } elseif (is_array($validator['message'])) { + if (count($validator['message']) > 1) { + $args = array_slice($validator['message'], 1); + } else { + $args = $validator['rule']; + } + $validator['message'] = __d($validationDomain, $validator['message'][0], $args); + } + $this->invalidate($fieldName, $validator['message']); + + if ($validator['last']) { + break; + } + } + } + } + } + } + $this->validate = $_validate; + return $this->validationErrors; + } + +/** + * Runs validation for hasAndBelongsToMany associations that have 'with' keys + * set. And data in the set() data set. + * + * @param array $options Array of options to use on Valdation of with models + * @return boolean Failure of validation on with models. + * @see Model::validates() + */ + protected function _validateWithModels($options) { + $valid = true; + foreach ($this->hasAndBelongsToMany as $assoc => $association) { + if (empty($association['with']) || !isset($this->data[$assoc])) { + continue; + } + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + $data = $this->data[$assoc]; + + $newData = array(); + foreach ((array)$data as $row) { + if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + if (empty($newData)) { + continue; + } + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id; + $this->{$join}->create($data); + $valid = ($valid && $this->{$join}->validates($options)); + } + } + return $valid; + } +/** + * Marks a field as invalid, optionally setting the name of validation + * rule (in case of multiple validation for field) that was broken. + * + * @param string $field The name of the field to invalidate + * @param mixed $value Name of validation rule that was not failed, or validation message to + * be returned. If no validation key is provided, defaults to true. + * @return void + */ + public function invalidate($field, $value = true) { + if (!is_array($this->validationErrors)) { + $this->validationErrors = array(); + } + $this->validationErrors = Set::insert($this->validationErrors, $field, $value); # QUICKAPPS MOD + } + +/** + * Returns true if given field name is a foreign key in this model. + * + * @param string $field Returns true if the input string ends in "_id" + * @return boolean True if the field is a foreign key listed in the belongsTo array. + */ + public function isForeignKey($field) { + $foreignKeys = array(); + if (!empty($this->belongsTo)) { + foreach ($this->belongsTo as $assoc => $data) { + $foreignKeys[] = $data['foreignKey']; + } + } + return in_array($field, $foreignKeys); + } + +/** + * Escapes the field name and prepends the model name. Escaping is done according to the + * current database driver's rules. + * + * @param string $field Field to escape (e.g: id) + * @param string $alias Alias for the model (e.g: Post) + * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`). + */ + public function escapeField($field = null, $alias = null) { + if (empty($alias)) { + $alias = $this->alias; + } + if (empty($field)) { + $field = $this->primaryKey; + } + $db = $this->getDataSource(); + if (strpos($field, $db->name($alias) . '.') === 0) { + return $field; + } + return $db->name($alias . '.' . $field); + } + +/** + * Returns the current record's ID + * + * @param integer $list Index on which the composed ID is located + * @return mixed The ID of the current record, false if no ID + */ + public function getID($list = 0) { + if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) { + return false; + } + + if (!is_array($this->id)) { + return $this->id; + } + + if (empty($this->id)) { + return false; + } + + if (isset($this->id[$list]) && !empty($this->id[$list])) { + return $this->id[$list]; + } elseif (isset($this->id[$list])) { + return false; + } + + foreach ($this->id as $id) { + return $id; + } + + return false; + } + +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + */ + public function getLastInsertID() { + return $this->getInsertID(); + } + +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + */ + public function getInsertID() { + return $this->_insertID; + } + +/** + * Sets the ID of the last record this model inserted + * + * @param mixed $id Last inserted ID + * @return void + */ + public function setInsertID($id) { + $this->_insertID = $id; + } + +/** + * Returns the number of rows returned from the last query. + * + * @return integer Number of rows + */ + public function getNumRows() { + return $this->getDataSource()->lastNumRows(); + } + +/** + * Returns the number of rows affected by the last query. + * + * @return integer Number of rows + */ + public function getAffectedRows() { + return $this->getDataSource()->lastAffected(); + } + +/** + * Sets the DataSource to which this model is bound. + * + * @param string $dataSource The name of the DataSource, as defined in app/Config/database.php + * @return boolean True on success + * @throws MissingConnectionException + */ + public function setDataSource($dataSource = null) { + $oldConfig = $this->useDbConfig; + + if ($dataSource != null) { + $this->useDbConfig = $dataSource; + } + $db = ConnectionManager::getDataSource($this->useDbConfig); + if (!empty($oldConfig) && isset($db->config['prefix'])) { + $oldDb = ConnectionManager::getDataSource($oldConfig); + + if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + } elseif (isset($db->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + + if (empty($db) || !is_object($db)) { + throw new MissingConnectionException(array('class' => $this->name)); + } + } + +/** + * Gets the DataSource to which this model is bound. + * + * @return DataSource A DataSource object + */ + public function getDataSource() { + if (!$this->_sourceConfigured && $this->useTable !== false) { + $this->_sourceConfigured = true; + $this->setSource($this->useTable); + } + return ConnectionManager::getDataSource($this->useDbConfig); + } + +/** + * Get associations + * + * @return array + */ + public function associations() { + return $this->_associations; + } + +/** + * Gets all the models with which this model is associated. + * + * @param string $type Only result associations of this type + * @return array Associations + */ + public function getAssociated($type = null) { + if ($type == null) { + $associated = array(); + foreach ($this->_associations as $assoc) { + if (!empty($this->{$assoc})) { + $models = array_keys($this->{$assoc}); + foreach ($models as $m) { + $associated[$m] = $assoc; + } + } + } + return $associated; + } elseif (in_array($type, $this->_associations)) { + if (empty($this->{$type})) { + return array(); + } + return array_keys($this->{$type}); + } else { + $assoc = array_merge( + $this->hasOne, + $this->hasMany, + $this->belongsTo, + $this->hasAndBelongsToMany + ); + if (array_key_exists($type, $assoc)) { + foreach ($this->_associations as $a) { + if (isset($this->{$a}[$type])) { + $assoc[$type]['association'] = $a; + break; + } + } + return $assoc[$type]; + } + return null; + } + } + +/** + * Gets the name and fields to be used by a join model. This allows specifying join fields + * in the association definition. + * + * @param string|array $assoc The model to be joined + * @param array $keys Any join keys which must be merged with the keys queried + * @return array + */ + public function joinModel($assoc, $keys = array()) { + if (is_string($assoc)) { + list(, $assoc) = pluginSplit($assoc); + return array($assoc, array_keys($this->{$assoc}->schema())); + } elseif (is_array($assoc)) { + $with = key($assoc); + return array($with, array_unique(array_merge($assoc[$with], $keys))); + } + trigger_error( + __d('cake_dev', 'Invalid join model settings in %s', $model->alias), + E_USER_WARNING + ); + } + +/** + * Called before each find operation. Return false if you want to halt the find + * call, otherwise return the (modified) query data. + * + * @param array $queryData Data used to execute this query, i.e. conditions, order, etc. + * @return mixed true if the operation should continue, false if it should abort; or, modified + * $queryData to continue with new $queryData + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeFind-1049 + */ + public function beforeFind($queryData) { + return true; + } + +/** + * Called after each find operation. Can be used to modify any results returned by find(). + * Return value should be the (modified) results. + * + * @param mixed $results The results of the find operation + * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association) + * @return mixed Result of the find operation + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterFind-1050 + */ + public function afterFind($results, $primary = false) { + return $results; + } + +/** + * Called before each save operation, after validation. Return a non-true result + * to halt the save. + * + * @param array $options + * @return boolean True if the operation should continue, false if it should abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeSave-1052 + */ + public function beforeSave($options = array()) { + return true; + } + +/** + * Called after each successful save operation. + * + * @param boolean $created True if this save created a new record + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterSave-1053 + */ + public function afterSave($created) { + } + +/** + * Called before every deletion operation. + * + * @param boolean $cascade If true records that depend on this record will also be deleted + * @return boolean True if the operation should continue, false if it should abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeDelete-1054 + */ + public function beforeDelete($cascade = true) { + return true; + } + +/** + * Called after every deletion operation. + * + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#afterDelete-1055 + */ + public function afterDelete() { + } + +/** + * Called during validation operations, before validation. Please note that custom + * validation rules can be defined in $validate. + * + * @param array $options Options passed from model::save(), see $options of model::save(). + * @return boolean True if validate operation should continue, false to abort + * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeValidate-1051 + */ + public function beforeValidate($options = array()) { + return true; + } + +/** + * Called when a DataSource-level error occurs. + * + * @return void + * @link http://book.cakephp.org/view/1048/Callback-Methods#onError-1056 + */ + public function onError() { + } + +/** + * Clears cache for this model. + * + * @param string $type If null this deletes cached views if Cache.check is true + * Will be used to allow deleting query cache also + * @return boolean true on delete + * @todo + */ + protected function _clearCache($type = null) { + if ($type === null) { + if (Configure::read('Cache.check') === true) { + $assoc[] = strtolower(Inflector::pluralize($this->alias)); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias))); + foreach ($this->_associations as $key => $association) { + foreach ($this->$association as $key => $className) { + $check = strtolower(Inflector::pluralize($className['className'])); + if (!in_array($check, $assoc)) { + $assoc[] = strtolower(Inflector::pluralize($className['className'])); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className']))); + } + } + } + clearCache($assoc); + return true; + } + } else { + //Will use for query cache deleting + } + } +} diff --git a/app/Locale/spa/LC_MESSAGES/core.po b/app/Locale/spa/LC_MESSAGES/core.po new file mode 100644 index 00000000..495895ee --- /dev/null +++ b/app/Locale/spa/LC_MESSAGES/core.po @@ -0,0 +1,64 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-07-28 21:04+0100\n" +"PO-Revision-Date: \n" +"Last-Translator: Christopher C \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Country: SPAIN\n" +"X-Poedit-KeywordsList: _e;__e;__;_\n" +"X-Poedit-Basepath: ../../../\n" +"X-Poedit-SearchPath-0: .\n" + +msgid "Dashboard" +msgstr "Resumen" + +msgid "Structure" +msgstr "Estructura" + +msgid "Content" +msgstr "Contenidos" + +msgid "Appearance" +msgstr "Apariencia" + +msgid "Modules" +msgstr "Módulos" + +msgid "Users" +msgstr "Usuarios" + +msgid "Configuration" +msgstr "Configuración" + +msgid "Help" +msgstr "Ayuda" + +msgid "Hidden" +msgstr "Oculto" + +msgid "Languages" +msgstr "Idiomas" + +msgid "View Site" +msgstr "Ver Sitio" + +msgid "Log out" +msgstr "Cerrar sesión" + +msgid "Logout" +msgstr "Cerrar sesión" + +msgid "My account" +msgstr "Mi cuenta" + +msgid "« Previous " +msgstr "« Anterior " + +msgid " Next »" +msgstr " Siguiente »" + diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php new file mode 100644 index 00000000..d8b2b106 --- /dev/null +++ b/app/Model/AppModel.php @@ -0,0 +1,205 @@ + + * @link http://cms.quickapps.es + */ +class AppModel extends Model { + public $cacheQueries = false; + public $listeners = array(); + public $events = array(); + public $actsAs = array( + 'WhoDidIt' => array( + 'auth_session' => 'Auth.User.id', + 'user_model' => 'User.User' + ) + ); + public $Options = array( + 'break' => false, + 'breakOn' => false, + 'collectReturn' => false + ); + private $__Options = array( + 'break' => false, + 'breakOn' => false, + 'collectReturn' => false + ); + + public function __construct($id = false, $table = null, $ds = null) { + $this->__loadHooks(); + parent::__construct($id, $table, $ds); + $this->__loadHookEvents(); + } + +/** + * Marks a field as invalid, optionally setting the name of validation + * rule (in case of multiple validation for field) that was broken. + * + * @param string $field The name of the field to invalidate + * @param mixed $value Name of validation rule that was not failed, or validation message to + * be returned. If no validation key is provided, defaults to true. + * @return void + */ + public function invalidate($field, $value = true) { + $value = is_string($value) ? __t($value) : $value; + parent::invalidate($field, $value); + + return; + } + +/** + * Chech if hook exists + * + * @param string $hook Name of the hook to check + * @return bool + */ + public function hook_defined($hook) { + return (in_array($hook, $this->events) == true); + } + +/** + * Trigger a callback method on every HookBehavior. + * + * ### Options + * + * - `breakOn` Set to the value or values you want the callback propagation to stop on. + * Can either be a scalar value, or an array of values to break on. + * Defaults to `false`. + * + * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value + * will be returned. If used in combination with `collectReturn` the collected results will be returned. + * Defaults to `false`. + * + * - `collectReturn` Set to true to collect the return of each object into an array. + * This array of return values will be returned from the hook() call. Defaults to `false`. + * + * - `alter` Allows each callback gets called on to modify the parameters to the next object. + * Defaults to true. + * + * @param string $event name of the hook to call + * @param mixed $data data for the triggered callback + * @param array $option Array of options + * @return mixed Either the last result or all results if collectReturn is on. Or null in case of no response + */ + public function hook($hook, &$data = array(), $options = array()) { + return $this->__dispatchEvent($hook, $data, $options); + } + +/** + * Overwrite default options for Hook dispatcher. + * Useful when calling a hook with non-parameter and custom options. + * + * Watch out!: Hook dispatcher automatic reset its default options to + * the original ones after `hook()` is invoked. + * Means that if you need to call more than one hook (consecutive) with no parameters and + * same options ** you must call `setHookOptions()` after each hook() ** + * + * ### Usage + * For example in any controller action: + * {{{ + * $this->setHookOptions(array('collectReturn' => false)); + * $response = $this->hook('collect_hook_with_no_parameters'); + * + * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); + * $response2 = $this->hook('OTHER_collect_hook_with_no_parameters'); + * }}} + * + * @param array $options Array of options to overwrite + * @return void + */ + public function setHookOptions($options) { + $this->Options = Set::merge($this->Options, $options); + } + +/** + * Dispatch Component-hooks from all the plugins and core + * + * @see AppModel::hook() + * @return mixed Either the last result or all results if collectReturn is on. Or NULL in case of no response + */ + private function __dispatchEvent($event, &$data = array(), $options = array()) { + $options = array_merge($this->Options, (array)$options); + $collected = array(); + + if (!$this->hook_defined($event)) { + $this->__resetOptions(); + + return null; + } + + foreach ($this->listeners as $object => $methods) { + foreach ($methods as $method) { + if ($method == $event && is_callable(array($this->Behaviors->{$object}, $method))) { + $result = @call_user_func(array($this->Behaviors->{$object}, $event), $data); + + if ($options['collectReturn'] === true) { + $collected[] = $result; + } + + if ($options['break'] && + ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true))) + ) { + $this->__resetOptions(); + + return $result; + } + } + } + } + + if (empty($collected) && in_array($result, array('', null), true)) { + $this->__resetOptions(); + + return null; + } + + $this->__resetOptions(); + + return $options['collectReturn'] ? $collected : $result; + } + + private function __resetOptions() { + if ($this->Options !== $this->__Options) { + $this->Options = $this->__Options; + } + } + + private function __loadHooks() { + $b = Configure::read('Hook.behaviors'); + + if (!$b){ + return false; # fix for AppController __preloadHooks() + } + + foreach ($b as $hook) { + $this->actsAs[$hook] = array(); + } + } + + private function __loadHookEvents() { + foreach ($this->actsAs as $behavior => $b_data) { + $behavior = strpos($behavior, '.') !== false ? substr($behavior, strpos($behavior, '.')+1) : $behavior; + + if (strpos($behavior, 'Hook')) { + $methods = array(); + $_methods = get_this_class_methods($this->Behaviors->{$behavior}); + + foreach ($_methods as $method) { + $methods[] = $method; + } + + $this->listeners[$behavior] = $methods; + $this->events = array_merge($this->events, $methods); + } + } + + $this->events = array_unique($this->events); + + return true; + } +} \ No newline at end of file diff --git a/app/Model/Behavior/SerializedBehavior.php b/app/Model/Behavior/SerializedBehavior.php new file mode 100644 index 00000000..514a017d --- /dev/null +++ b/app/Model/Behavior/SerializedBehavior.php @@ -0,0 +1,103 @@ + + * @link http://cms.quickapps.es + */ +class SerializedBehavior extends ModelBehavior { + +/** + * Fields + * + * @var array + * @access protected + */ + private $fields = array(); + +/** + * Initiate Serialized behavior + * + * @param object $Model instance of model + * @param array $config array of configuration settings. + * @return void + * @access public + */ + public function setup($Model, $config = array()) { + if (is_string($config)) { + $config = array($config); + } + + $this->fields = array_merge($this->fields, $config); + } + + public function afterFind(&$Model, $results, $primary) { + $_results = $results; + + if (isset($_results[0][$Model->alias])) { + foreach ($_results as $rkey => &$record) { + foreach ($this->fields as $field) { + if (isset($record[$Model->alias][$field]) && + !empty($record[$Model->alias][$field]) && + is_string($record[$Model->alias][$field]) + ) { + $record[$Model->alias][$field] = @unserialize($record[$Model->alias][$field]); + } + } + } + } else { + foreach ($this->fields as $field) { + if (isset($_results[$Model->alias][$field]) && + !empty($_results[$Model->alias][$field]) && + is_string($_results[$Model->alias][$field]) + ) { + $_results[$Model->alias][$field] = @unserialize($_results[$Model->alias][$field]); + } + } + } + + return $_results; + } + + public function beforeSave($Model) { + if (isset($Model->data[$Model->alias][0])) { + foreach ($Model->data[$Model->alias] as &$record) { + foreach ($record as $field => &$data) { + if (!in_array($field, $this->fields)) { + continue; + } + + $data = $this->serialize($data); + } + } + } elseif (isset($Model->data[0])) { + foreach ($Model->data as $key => &$row) { + foreach ($row as $field => &$value) { + if (!in_array($field, $this->fields)) { + continue; + } + + $value = $this->serialize($value); + } + } + } else { + foreach ($Model->data[$Model->alias] as $field => &$data) { + if (!in_array($field, $this->fields)) { + continue; + } + + $data = $this->serialize($data); + } + } + + return true; + } + + public function serialize($data) { + return (empty($data) ? @serialize(array()): @serialize($data)); + } +} \ No newline at end of file diff --git a/app/Model/Behavior/SluggableBehavior.php b/app/Model/Behavior/SluggableBehavior.php new file mode 100644 index 00000000..2e2b8cb4 --- /dev/null +++ b/app/Model/Behavior/SluggableBehavior.php @@ -0,0 +1,195 @@ + array('title'), 'slug' => 'slug', 'separator' => '-', 'length' => 200, 'overwrite' => true, 'translation' => null); + + if (!isset($this->__settings[$Model->alias])) + { + $this->__settings[$Model->alias] = $default; + } + + $this->__settings[$Model->alias] = am($this->__settings[$Model->alias], (is_array($settings) ? $settings : array())); + } + + /** + * Run before a model is saved, used to set up slug for model. + * + * @param object $Model Model about to be saved. + * @return boolean true if save should proceed, false otherwise + * @access public + */ + function beforeSave(&$Model) + { + $return = parent::beforeSave($Model); + + // Make label fields an array + + if (!is_array($this->__settings[$Model->alias]['label'])) + { + $this->__settings[$Model->alias]['label'] = array($this->__settings[$Model->alias]['label']); + } + + // Make sure all label fields are available + + foreach ($this->__settings[$Model->alias]['label'] as $field) + { + if (!$Model->hasField($field)) + { + return $return; + } + } + + // See if we should be generating a slug + if ($Model->hasField($this->__settings[$Model->alias]['slug']) && ($this->__settings[$Model->alias]['overwrite'] || empty($Model->id))) + { + // Build label out of data in label fields, if available, or using a default slug otherwise + + $label = ''; + foreach ($this->__settings[$Model->alias]['label'] as $field) + { + if (!empty($Model->data[$Model->alias][$field])) + { + $label .= (!empty($label) ? ' ' : '') . $Model->data[$Model->alias][$field]; + } + } + + // Keep on going only if we've got something to slug + + if (!empty($label)) + { + // Get the slug + + $slug = $this->__slug($label, $this->__settings[$Model->alias]); + + // Look for slugs that start with the same slug we've just generated + // Bug 1 + // The following line is not working any more: + // $conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'] => 'LIKE ' . $slug . '%'); + + // Fix for Bug1: + //$conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'] => $slug); // Fix 1 + $conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'].' LIKE' => $slug.'%'); // Fix 2 + + if (!empty($Model->id)) + { + // Bug 2 + // The following line is not working any more: + // $conditions[$Model->alias . '.' . $Model->primaryKey] = '!= ' . $Model->id; + + // Fix for Bug 2: + $conditions['not'] = array( + $Model->alias . '.' . $Model->primaryKey => + $Model->id + ); + } + $result = $Model->find('all', array('conditions' => $conditions, 'fields' => array($Model->primaryKey, $this->__settings[$Model->alias]['slug']), 'recursive' => -1)); + $sameUrls = null; + + if (!empty($result)) + { + $sameUrls = Set::extract($result, '{n}.' . $Model->alias . '.' . $this->__settings[$Model->alias]['slug']); + } + + // If we have collissions + + if (!empty($sameUrls)) + { + $begginingSlug = $slug; + $index = 1; + + // Attach an ending incremental number until we find a free slug + + while($index > 0) + { + if (!in_array($begginingSlug . $this->__settings[$Model->alias]['separator'] . $index, $sameUrls)) + { + $slug = $begginingSlug . $this->__settings[$Model->alias]['separator'] . $index; + $index = -1; + } + + $index++; + } + } + + // Now set the slug as part of the model data to be saved, making sure that + // we are on the white list of fields to be saved + + if (!empty($Model->whitelist) && !in_array($this->__settings[$Model->alias]['slug'], $Model->whitelist)) + { + $Model->whitelist[] = $this->__settings[$Model->alias]['slug']; + } + + $Model->data[$Model->alias][$this->__settings[$Model->alias]['slug']] = $slug; + } + } + + return $return; + } + + /** + * Generate a slug for the given string using specified settings. + * + * @param string $string String from where to generate slug + * @param array $settings Settings to use (looks for 'separator' and 'length') + * @return string Slug for given string + * @access private + */ + function __slug($string, $settings) { + $string = Inflector::slug($string, $settings['separator']); + $string = strtolower($string); + if (strlen($string) > $settings['length']) + $string = substr($string, 0, $settings['length']); + return $string; + } +} \ No newline at end of file diff --git a/app/Model/Behavior/WhoDidItBehavior.php b/app/Model/Behavior/WhoDidItBehavior.php new file mode 100644 index 00000000..c6a563a4 --- /dev/null +++ b/app/Model/Behavior/WhoDidItBehavior.php @@ -0,0 +1,99 @@ + 'Auth', //name of Auth session key + 'user_model' => 'User', //name of User model + 'created_by_field' => 'created_by', //the name of the "created_by" field in DB (default 'created_by') + 'modified_by_field' => 'modified_by', //the name of the "modified_by" field in DB (default 'modified_by') + 'auto_bind' => true //automatically bind the model to the User model (default true) + ); + +/** + * Initiate WhoMadeIt Behavior + * + * @param object $model + * @param array $config behavior settings you would like to override + * @return void + * @access public + */ + function setup(&$model, $config = array()) { + //assigne default settings + $this->settings[$model->alias] = $this->_defaults; + + //merge custom config with default settings + $this->settings[$model->alias] = array_merge($this->settings[$model->alias], (array)$config); + + $hasFieldCreatedBy = $model->hasField($this->settings[$model->alias]['created_by_field']); + $hasFieldModifiedBy = $model->hasField($this->settings[$model->alias]['modified_by_field']); + + $this->settings[$model->alias]['has_created_by'] = $hasFieldCreatedBy; + $this->settings[$model->alias]['has_modified_by'] = $hasFieldModifiedBy; + + //handles model binding to the User model + //according to the auto_bind settings (default true) + if ($this->settings[$model->alias]['auto_bind']) + { + if ($hasFieldCreatedBy) { + $commonBelongsTo = array( + 'CreatedBy' => array('className' => $this->settings[$model->alias]['user_model'], + 'foreignKey' => $this->settings[$model->alias]['created_by_field']) + ); + $model->bindModel(array('belongsTo' => $commonBelongsTo), false); + } + + if ($hasFieldModifiedBy) { + $commonBelongsTo = array( + 'ModifiedBy' => array('className' => $this->settings[$model->alias]['user_model'], + 'foreignKey' => $this->settings[$model->alias]['modified_by_field'])); + $model->bindModel(array('belongsTo' => $commonBelongsTo), false); + } + } + } + +/** + * Before save callback + * + * @param object $model Model using this behavior + * @return boolean True if the operation should continue, false if it should abort + * @access public + */ + function beforeSave(&$model) { + if ($this->settings[$model->alias]['has_created_by'] || $this->settings[$model->alias]['has_modified_by']) { + $AuthSession = $this->settings[$model->alias]['auth_session']; + + App::uses('CakeSession', 'Model/Datasource'); + $userId = CakeSession::read($AuthSession); + if ($userId) { + $data = array($this->settings[$model->alias]['modified_by_field'] => $userId); + if (!$model->exists()) { + $data[$this->settings[$model->alias]['created_by_field']] = $userId; + } + $model->set($data); + } + } + return true; + } +} \ No newline at end of file diff --git a/app/Model/Datasource/empty b/app/Model/Datasource/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/Config/bootstrap.php b/app/Plugin/block/Config/bootstrap.php new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/Config/routes.php b/app/Plugin/block/Config/routes.php new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/Controller/BlockAppController.php b/app/Plugin/block/Controller/BlockAppController.php new file mode 100644 index 00000000..c6872cd9 --- /dev/null +++ b/app/Plugin/block/Controller/BlockAppController.php @@ -0,0 +1,14 @@ + + * @link http://cms.quickapps.es + */ +class BlockAppController extends AppController { + +} \ No newline at end of file diff --git a/app/Plugin/block/Controller/BlockController.php b/app/Plugin/block/Controller/BlockController.php new file mode 100644 index 00000000..20ac143d --- /dev/null +++ b/app/Plugin/block/Controller/BlockController.php @@ -0,0 +1,19 @@ + + * @link http://cms.quickapps.es + */ +class BlockController extends BlockAppController { + public $name = 'Block'; + public $uses = array('Block.Block'); + + public function admin_index() { + $this->redirect('/admin/block/manage'); + } +} \ No newline at end of file diff --git a/app/Plugin/block/Controller/Component/BlockHookComponent.php b/app/Plugin/block/Controller/Component/BlockHookComponent.php new file mode 100644 index 00000000..6e6748e8 --- /dev/null +++ b/app/Plugin/block/Controller/Component/BlockHookComponent.php @@ -0,0 +1,32 @@ + + * @link http://cms.quickapps.es + */ +class BlockHookComponent extends Component { + public $Controller = null; + public $components = array('Hook'); + + public function initialize(&$Controller) { + $this->Controller = $Controller; + } + + public function blocks_list($params = array()) { + $params = array_merge($params, array('recursive' => 2)); + $Block = (isset($this->Controller->Block) && is_object($this->Controller->Block)) ? $this->Controller->Block : ClassRegistry::init('Block.Block'); + + $Block->Menu->unbindModel( + array('hasMany' => array('Block')) + ); + + $blocks = $Block->find('all', $params); + + return $blocks; + } +} \ No newline at end of file diff --git a/app/Plugin/block/Controller/ManageController.php b/app/Plugin/block/Controller/ManageController.php new file mode 100644 index 00000000..9b1c6387 --- /dev/null +++ b/app/Plugin/block/Controller/ManageController.php @@ -0,0 +1,214 @@ + + * @link http://cms.quickapps.es + */ +class ManageController extends BlockAppController { + public $name = 'Manage'; + public $uses = array('Block.Block', 'User.Role'); + + public function admin_index() { + $site_theme = $this->Block->find('all', + array( + 'conditions' => array( + 'OR' => array( + 'Block.themes_cache LIKE' => '%:' . Configure::read('Variable.site_theme') . ":%" + ) + ) + ) + ); + + $admin_theme = $this->Block->find('all', + array( + 'conditions' => array( + 'OR' => array( + 'Block.themes_cache LIKE' => '%:' . Configure::read('Variable.admin_theme') . ":%", + ) + ) + ) + ); + + $site_ids = (array)Set::extract('/Block/id', $site_theme); + $admin_ids = (array)Set::extract('/Block/id', $admin_theme); + + $unassigned = $this->Block->find('all', + array( + 'conditions' => array( + 'NOT' => array( + 'Block.id' => array_merge($site_ids, $admin_ids) + ) + ) + ) + ); + + $this->set('site_theme', (array)$site_theme); + $this->set('admin_theme', (array)$admin_theme); + $this->set('unassigned', (array)$unassigned); + $this->title(__t('Blocks')); + $this->set('themes', $this->__themesYaml()); + $this->setCrumb('/admin/block'); + } + + public function admin_move($block_region_id, $dir) { + if (in_array($dir, array('up', 'down'))) { + if ($dir == 'up') { + $this->Block->BlockRegion->move($block_region_id, 'up'); + } else { + $this->Block->BlockRegion->move($block_region_id, 'down'); + } + } + + $this->redirect($this->referer()); + } + + public function admin_clone($bid) { + $block = $this->Block->findById($bid) or $this->redirect($this->referer()); + $block = Set::filter($block); + $block['Block']['themes_cache'] = ''; + $block['Block']['title'] .= ' (' . __t('Clone') . ')'; + $block['Block']['clone_of'] = $block['Block']['id']; + + unset($block['Block']['id'], $block['BlockRegion']); + + if ($this->Block->saveAll($block)) { + $this->flashMsg(__t('Block has been cloned'), 'success'); + $this->redirect('/admin/block/manage/edit/' . $this->Block->id); + } else { + $this->flashMsg(__t('Block could not be cloned'), 'error'); + } + + $this->redirect($this->referer()); + } + + public function admin_edit($bid) { + if (isset($this->data['Block'])) { + $data = $this->data; + $data['Block']['locale'] = !empty($data['Block']['locale']) ? array_values($data['Block']['locale']) : array(); + $data['Block']['themes_cache'] = $this->__themesCache($data['BlockRegion']); + + if ($this->Block->saveAll($data, array('validate' => 'first'))) { # saveAll only will save Block related models! + if (isset($data['Module'])) { # save widgets variables + $this->Module->save($data['Module']); + Cache::delete('Modules'); + $this->Quickapps->loadModules(); + } + + if (isset($data['Variable'])) { + $this->Variable->save($data['Variable']); + Cache::delete('Variable'); + $this->Quickapps->loadVariables(); + } + + $this->flashMsg(__t('Block has been saved'), 'success'); + } else { + $this->flashMsg(__t('Block could not be saved. Please, try again.'), 'error'); + } + + $this->redirect("/admin/block/manage/edit/{$bid}"); + } + + $themes = $this->__themesYaml(); + + foreach ($themes as $theme => $yaml) { + $_regions["{$yaml['info']['name']}@|@{$theme}"] = array(); + + foreach ($yaml['regions'] as $name => $title) { + $_regions["{$yaml['info']['name']}@|@{$theme}"]["{$name}"] = $title; + } + } + + $this->data = $this->Block->findById($bid) or $this->redirect('/admin/block/manage'); + + $this->title(__t('Editing Block')); + $this->setCrumb('/admin/block'); + $this->set('regions', $_regions); + $this->set('roles', $this->Role->find('list')); + } + + public function admin_add() { + $this->title(__t('Add new block')); + $this->setCrumb('/admin/block'); + $this->setCrumb(array(__t('New block'), '')); + + if (isset($this->data['Block'])) { + $data = $this->data; + + foreach ($data['BlockRegion'] as $key => $br) { + if (empty($br['region'])) { + unset($data['BlockRegion'][$key]); + } + } + + $data['Block']['module'] = 'block'; + $data['Block']['locale'] = !empty($data['Block']['locale']) ? array_values($data['Block']['locale']) : array(); + $data['Block']['themes_cache'] = $this->__themesCache($data['BlockRegion']); + + if ($this->Block->saveAll($data, array('validate' => 'first'))) { + $this->Block->BlockRegion->deleteAll( array('region' => '')); + $this->flashMsg(__t('Block has been saved'), 'success'); + $this->redirect("/admin/block/manage/edit/{$this->Block->id}"); + } else { + $this->flashMsg(__t('Block could not be saved. Please, try again.'), 'error'); + } + } + + $themes = $this->__themesYaml(); + + foreach ($themes as $theme => $yaml) { + $_regions["{$yaml['info']['name']}@|@{$theme}"] = array(); + + foreach ($yaml['regions'] as $name => $title) { + $_regions["{$yaml['info']['name']}@|@{$theme}"]["{$name}"] = $title; + } + } + + $this->set('regions', $_regions); + $this->set('roles', $this->Role->find('list')); + } + + public function admin_delete($id) { + $block = $this->Block->findById($id); + + if (!$block || ($block['Block']['module'] != 'block' && !$block['Block']['module'])) { + $this->redirect('/admin'); + } else { + $this->Block->delete($id); + $this->redirect($this->referer()); + } + } + + private function __themesCache($BlockRegion) { + $o = array(); + + foreach ($BlockRegion as $key => $r) { + if (!empty($r['region'])) { + $o[] = $r['theme']; + } + } + $o = ':' . implode(":", array_unique($o)) . ':'; + + return preg_replace('/\:{2,}/', ':', $o); + } + + private function __themesYaml() { + $return = array(); + $folder = new Folder; + $folder->path = APP . 'View' . DS . 'Themed'; + $folders = $folder->read(); + + foreach ($folders[0] as $theme) { + if (APP . 'View' . DS . 'Themed' . DS . $theme . DS . "{$theme}.yaml") { + $yaml = Spyc::YAMLLoad(APP . 'View' . DS . 'Themed' . DS . $theme . DS . "{$theme}.yaml"); + $return[$theme] = $yaml; + } + } + + return $return; + } +} \ No newline at end of file diff --git a/app/Plugin/block/Lib/empty b/app/Plugin/block/Lib/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po b/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po new file mode 100644 index 00000000..928e4c64 --- /dev/null +++ b/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po @@ -0,0 +1,136 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-07-28 21:04+0100\n" +"PO-Revision-Date: \n" +"Last-Translator: Christopher C \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Country: SPAIN\n" +"X-Poedit-KeywordsList: _e;__e;__;_\n" +"X-Poedit-Basepath: ../../../\n" +"X-Poedit-SearchPath-0: .\n" + +msgid "New Block" +msgstr "Nuevo Bloque" + +msgid "Save block" +msgstr "Guardar bloque" + +msgid "Unassigned" +msgstr "No Asignados" + +msgid "Blocks" +msgstr "Bloques" + +msgid "clone" +msgstr "clonar" + +msgid "configure" +msgstr "configurar" + +msgid "delete" +msgstr "eliminar" + +msgid "move up" +msgstr "mover arriba" + +msgid "move down" +msgstr "mover abajo" + +msgid "Block" +msgstr "Bloque" + +msgid "Region" +msgstr "Región" + +msgid "Actions" +msgstr "Acciones" + +msgid "Duplicate this block?" +msgstr "Duplicar este bloque?" + +msgid "Active" +msgstr "Activo" + +msgid "Block title" +msgstr "Título de bloque" + +msgid "The title of the block as shown to the user." +msgstr "Título de bloque que se mostrara al usuario." + +msgid "Block description *" +msgstr "Descripción de bloque *" + +msgid "A brief description of your block. Used on the Blocks administration page." +msgstr "Breve descripción del bloque. Utilizado en la sección de gestión de bloques." + +msgid "Block body *" +msgstr "Cuerpo de bloque *" + +msgid "Language" +msgstr "Idioma" + +msgid "Translations" +msgstr "Traducciones" + +msgid "Show this block for these languages" +msgstr "Mostrar este bloque para los siguientes idiomas" + +msgid "If no language is selected, block will show regardless of language." +msgstr "Si ningún idioma se indica, el bloque se mostrara independientemente del idioma." + +msgid "Visibility settings" +msgstr "Opciones de visualización" + +msgid "Theme Region" +msgstr "Región de Theme" + +msgid "Specify in which themes and regions this block is displayed." +msgstr "Indica en que theme y región este bloque se debe mostrar." + +msgid "Pages" +msgstr "Páginas" + +msgid "All pages except those listed" +msgstr "En todas las páginas excepto en las listadas" + +msgid "Only the listed pages" +msgstr "Sólo en las páginas listadas" + +msgid "Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are blog for the blog page and blog/* for every blog entry. '/' is the front page." +msgstr "Indique páginas usando su ruta. Ingrese una ruta por línea. El caracter '*' es un comodín. Ejemplos de rutas son blog para la página de blog y /blog/* para cualquier entrada de blog. '/' es la página principal." + +msgid "Roles" +msgstr "Roles" + +msgid "Show block for specific roles" +msgstr "Mostrar bloque para los roles indicados" + +msgid "Show this block only for the selected role(s). If you select no roles, the block will be visible to all users." +msgstr "Muestra este bloque solo para el(los) rol(es) seleccionado(s). Si no se indica(n) rol(es), el bloque será visible para todos los usuarios." + +msgid "Advanced Options" +msgstr "Opciones Avanzadas" + +msgid "Block class suffix" +msgstr "Sufijo de clase para bloque" + +msgid "A suffix to be applied to the CSS class of the block. This allows for individual block styling." +msgstr "Un sufijo para aplicar a las clases CSS del bloque. Esto permite estilizar individualmente el bloque." + +msgid "Editing Block" +msgstr "Editando Bloque" + +msgid "Add new block" +msgstr "Agregar nuevo bloque" + +msgid "Pages on which this PHP code returns TRUE (experts only)" +msgstr "Páginas en las que este código PHP retorne TRUE (sólo expertos)" + +msgid "If the PHP option is chosen, enter PHP code between <?php ?>. Note that executing incorrect PHP code can break your QuickApps site." +msgstr "Si se elige la opcion PHP, ingrese el código PHP entre <?php ?>. La ejecución de código PHP incorrecto puede romper su sitio QuickApps." + diff --git a/app/Plugin/block/Model/Behavior/BlockHookBehavior.php b/app/Plugin/block/Model/Behavior/BlockHookBehavior.php new file mode 100644 index 00000000..71eb9d48 --- /dev/null +++ b/app/Plugin/block/Model/Behavior/BlockHookBehavior.php @@ -0,0 +1,14 @@ + + * @link http://cms.quickapps.es + */ +class BlockHookBehavior extends ModelBehavior { + +} \ No newline at end of file diff --git a/app/Plugin/block/Model/Block.php b/app/Plugin/block/Model/Block.php new file mode 100644 index 00000000..9b165ee5 --- /dev/null +++ b/app/Plugin/block/Model/Block.php @@ -0,0 +1,66 @@ + + * @link http://cms.quickapps.es + */ +class Block extends BlockAppModel { + public $name = 'Block'; + public $useTable = 'blocks'; + public $primaryKey = 'id'; + public $actsAs = array('Serialized' => array('locale', 'settings', 'params')); + + public $hasOne = array( + 'BlockCustom' => array( + 'className' => 'Block.BlockCustom', + 'dependent' => true + ) + ); + + public $hasMany = array( + 'BlockRegion' => array( + 'className' => 'Block.BlockRegion', + 'dependent' => true + ) + ); + + public $belongsTo = array( + 'Menu' => array( + 'className' => 'Menu.Menu', + 'foreignKey' => 'delta', + 'conditions' => 'Block.module = "menu"', + 'dependent' => false + ) + ); + + public $hasAndBelongsToMany = array( + 'Role' => array( + 'joinTable' => 'block_roles', + 'className' => 'User.Role', + 'foreignKey' => 'block_id', + 'associationForeignKey' => 'user_role_id', + 'unique' => true, + 'dependent' => false + ) + ); + + public function beforeSave() { + /* get New delta */ + if (!isset($this->data['Block']['id'])) { # new record + if ($this->data['Block']['module'] == 'menu' || isset($this->data['Block']['delta'])) { + return true; + } + + $max_delta = $this->find('first', array('conditions' => array('Block.module' => 'block'), 'fields' => array('delta'), 'order' => array('delta' => 'DESC'))); + $max_delta = !empty($max_delta) ? $max_delta['Block']['delta'] + 1 : 1; + $this->data['Block']['delta'] = $max_delta; + } + + return true; + } +} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockAppModel.php b/app/Plugin/block/Model/BlockAppModel.php new file mode 100644 index 00000000..b4fb1fcc --- /dev/null +++ b/app/Plugin/block/Model/BlockAppModel.php @@ -0,0 +1,14 @@ + + * @link http://cms.quickapps.es + */ +class BlockAppModel extends AppModel { + +} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockCustom.php b/app/Plugin/block/Model/BlockCustom.php new file mode 100644 index 00000000..b514d991 --- /dev/null +++ b/app/Plugin/block/Model/BlockCustom.php @@ -0,0 +1,20 @@ + + * @link http://cms.quickapps.es + */ +class BlockCustom extends BlockAppModel { + public $name = 'BlockCustom'; + public $useTable = "block_custom"; + public $primaryKey = "block_id"; + public $validate = array( + 'description' => array('required' => true, 'allowEmpty' => false, 'rule' => 'notEmpty', 'message' => 'Invalid description'), + 'body' => array('required' => true, 'allowEmpty' => false, 'rule' => 'notEmpty', 'message' => 'Invalid block body'), + ); +} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockRegion.php b/app/Plugin/block/Model/BlockRegion.php new file mode 100644 index 00000000..5383794a --- /dev/null +++ b/app/Plugin/block/Model/BlockRegion.php @@ -0,0 +1,68 @@ + + * @link http://cms.quickapps.es + */ +class BlockRegion extends BlockAppModel { + public $name = 'BlockRegion'; + public $useTable = 'block_regions'; + public $order = array('BlockRegion.ordering' => 'ASC'); + public $primaryKey = 'id'; + + public function beforeSave() { + if (!isset($this->data['BlockRegion']['id'])) { + $r = $this->data['BlockRegion']['region']; + $t = $this->data['BlockRegion']['theme']; + $c = $this->find('count', array('conditions' => array('BlockRegion.theme' => $t, 'BlockRegion.region' => $r))); + $this->data['BlockRegion']['ordering'] = $c+1; + } + + return true; + } + + public function move($id, $dir = 'up') { + if (!$record = $this->findById($id)) { + return false; + } + + $nodes = $this->find('all', + array( + 'conditions' => array( + 'BlockRegion.theme' => $record['BlockRegion']['theme'], + 'BlockRegion.region' => $record['BlockRegion']['region'] + ), + 'order' => array('BlockRegion.ordering' => 'ASC'), + 'fields' => array('id', 'ordering'), + 'recursive' => -1 + ) + ); + $ids = Set::extract('/BlockRegion/id', $nodes); + + if (($dir == 'down' && $ids[count($ids)-1] == $record['BlockRegion']['id']) || + ($dir == 'up' && $ids[0] == $record['BlockRegion']['id']) + ) { #edge -> cant go down/up + return false; + } + + $position = array_search($record['BlockRegion']['id'], $ids); + $key = $dir == 'up' ? $position-1 : $position+1; + $tmp = $ids[$key]; + $ids[$key] = $ids[$position]; + $ids[$position] = $tmp; + + $i = 1; + + foreach ($ids as $id) { + $this->id = $id; + $this->saveField('ordering', $i, false); + $i++; + } + } + +} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockRole.php b/app/Plugin/block/Model/BlockRole.php new file mode 100644 index 00000000..221fe814 --- /dev/null +++ b/app/Plugin/block/Model/BlockRole.php @@ -0,0 +1,16 @@ + + * @link http://cms.quickapps.es + */ +class BlockRole extends BlockAppModel { + public $name = 'BlockRole'; + public $useTable = "block_roles"; + public $primaryKey = null; +} \ No newline at end of file diff --git a/app/Plugin/block/View/Elements/help.ctp b/app/Plugin/block/View/Elements/help.ctp new file mode 100644 index 00000000..21ef2bdb --- /dev/null +++ b/app/Plugin/block/View/Elements/help.ctp @@ -0,0 +1,18 @@ +

About

+

+ The Block module allows you to create boxes of content, which are rendered into an area, or region, of one or more pages of a website. + The core Default administration theme, for example, implements the regions "Content", "Help", "Dashboard main", and "Dashboard sidebar", and a block may appear in any one of these regions. + The Blocks administration page provides an interface for assigning a block to a region, and for controlling the order of blocks within regions. +

+ +

Uses

+
+
Positioning content
+
When working with blocks, remember that all themes do not implement the same regions, or display regions in the same way. Blocks are positioned on a per-theme basis. Users with permissions can disable blocks. Disabled blocks are listed on the Blocks administration page, but are not displayed in any region.
+ +
Controlling visibility
+
Blocks can be configured to be visible only on certain pages, only to users of certain roles. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.
+ +
Creating custom blocks
+
Users with permissions can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.
+
\ No newline at end of file diff --git a/app/Plugin/block/View/Elements/toolbar.ctp b/app/Plugin/block/View/Elements/toolbar.ctp new file mode 100644 index 00000000..011dd23a --- /dev/null +++ b/app/Plugin/block/View/Elements/toolbar.ctp @@ -0,0 +1,6 @@ +Layout->toolbar($links); \ No newline at end of file diff --git a/app/Plugin/block/View/Helper/BlockHookHelper.php b/app/Plugin/block/View/Helper/BlockHookHelper.php new file mode 100644 index 00000000..55172b0d --- /dev/null +++ b/app/Plugin/block/View/Helper/BlockHookHelper.php @@ -0,0 +1,65 @@ + + * @link http://cms.quickapps.es + */ +class BlockHookHelper extends AppHelper { + // toolbar + public function beforeLayout($layoutFile) { + $show_on = (isset($this->request->params['plugin']) && $this->request->params['plugin'] == 'block' && $this->request->params['action'] != 'admin_add'); + $this->_View->Layout->blockPush(array('body' => $this->_View->element('toolbar')), 'toolbar', $show_on); + + return true; + } + + // hookTag, block rendering + public function block($options) { + extract($options); + + if (!isset($id)) { + return; + } + + if ($_block = Set::extract("/Block[id={$id}]/..", $this->_View->viewVars['Layout']['blocks'])) { + $block = $_block[0]; + } else { + $block = ClassRegistry::init('Block.Block')->findById($id); + } + + if (!$block ) { + return; + } + + $region = isset($region) ? $region : false; + $title = isset($title) ? int_val($title) : false; + + return $this->_View->Layout->block($block, array('title' => $title, 'region' => $region)); + } + + // hooktag + public function block_title($options) { + extract($options); + + if (!isset($id)) { + return; + } + + if ($_block = Set::extract("/Block[id={$id}]/..", $this->_View->viewVars['Layout']['blocks'])) { + $block = $_block[0]; + } else { + $block = ClassRegistry::init('Block.Block')->findById($id); + } + + if (!$block) { + return false; + } + + return $this->_View->Layout->hookTags($block['Block']['title']); + } +} \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_add.ctp b/app/Plugin/block/View/Manage/admin_add.ctp new file mode 100644 index 00000000..677b0110 --- /dev/null +++ b/app/Plugin/block/View/Manage/admin_add.ctp @@ -0,0 +1,75 @@ +Form->create('Block', array('url' => "/admin/block/manage/add")); ?> + + Html->useTag('fieldsetstart', __t('Content')); ?> + Form->hidden('status', array('value' => 1)); ?> + Form->input('Block.title', array('label' => __t('Block title'))); ?> + + + Form->input('BlockCustom.description', array('required' => 'required', 'label' => __t('Block description *'))); ?> + + + Form->input('BlockCustom.body', array('required' => 'required', 'label' => __t('Block body *'), 'class' => 'full', 'type' => 'textarea', 'after' => '')); ?> + Html->useTag('fieldsetend'); ?> + + + Html->useTag('fieldsetstart', __t('Language')); ?> + Html->useTag('fieldsetstart', __t('Translations')); ?> + + Form->input('locale', array('options' => $langs, 'type' => 'select', 'selected' => Set::extract('/Block/locale', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show this block for these languages'))); ?> + + Html->useTag('fieldsetend'); ?> + Html->useTag('fieldsetend'); ?> + + + Html->useTag('fieldsetstart', __t('Visibility settings')); ?> + Html->useTag('fieldsetstart', __t('Theme Region')); ?> +
+ $_regions ): ?> + + + Form->select("BlockRegion.{$i}.region", $_regions, array('empty' => __t('--None--'))) . "\n"; ?> + Form->hidden("BlockRegion.{$i}.theme", array('value' => $theme[1])) . "\n"; ?> + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Pages')); ?> + Form->input('visibility', + array( + 'type' => 'radio', + 'legend' => false, + 'value' => 0, + 'separator' => '
', + 'options' => array( + 0 => __t('All pages except those listed'), + 1 => __t('Only the listed pages'), + 2 => __t('Pages on which this PHP code returns TRUE (experts only)') + ) + ) + ); + ?> + + Form->input('pages', array('type' => 'textarea', 'label' => false, 'after' => '')); ?> + + + + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Roles')); ?> + Form->input('Role', array('options' => $roles, 'type' => 'select', 'multiple' => 'checkbox', 'label' => __t('Show block for specific roles'))); ?> + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Advanced Options')); ?> + Form->input('Block.params.class', array('label' => __t('Block class suffix'))); ?> + + Html->useTag('fieldsetend'); ?> + Html->useTag('fieldsetend'); ?> + + + Form->input(__t('Save block'), array('type' => 'submit')); ?> +Form->end(); ?> \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_edit.ctp b/app/Plugin/block/View/Manage/admin_edit.ctp new file mode 100644 index 00000000..fd1fc625 --- /dev/null +++ b/app/Plugin/block/View/Manage/admin_edit.ctp @@ -0,0 +1,98 @@ +Form->create('Block', array('url' => "/admin/block/manage/edit/{$this->data['Block']['id']}")); ?> + + Html->useTag('fieldsetstart', __t('Content')); ?> + Form->hidden('id'); ?> + Form->input('status', array('type' => 'checkbox', 'label' => __t('Active'))) . "\n"; ?> + Form->input('title', array('label' => __t('Block title'))); ?> + + + data['Block']['module'] === 'block'): # custom data only for custom blocks ?> + Form->input('BlockCustom.description', array('required' => 'required', 'label' => __t('Block description *'))); ?> + + Form->input('BlockCustom.body', array('required' => 'required', 'type' => 'textarea', 'class' => 'full', 'label' => __t('Block body *'), 'after' => '')); ?> + + Html->useTag('fieldsetend'); ?> + + data['Block']['module'] !== 'block'): ?> + Layout->attachModuleHooks($this->data['Block']['module']); ?> + data; ?> + Layout->hook("{$this->data['Block']['module']}_{$this->data['Block']['delta']}_settings", $data, array('collectReturn' => false))): # widget ?> + Html->useTag('fieldsetstart', 'Widget settings'); ?> + + Html->useTag('fieldsetend'); ?> + + Layout->deattachModuleHooks(Inflector::camelize($this->data['Block']['module'])); ?> + + + + Html->useTag('fieldsetstart', __t('Language')); ?> + Html->useTag('fieldsetstart', __t('Translations')); ?> + + Form->input('locale', array('options' => $langs, 'type' => 'select', 'selected' => Set::extract('/Block/locale', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show this block for these languages'))); ?> + + Html->useTag('fieldsetend'); ?> + Html->useTag('fieldsetend'); ?> + + + Html->useTag('fieldsetstart', __t('Visibility settings')); ?> + Html->useTag('fieldsetstart', __t('Theme Region')); ?> +
+ $_regions ): ?> + + + data); + $selected = !empty($selected) && isset($selected[0]) ? $selected[0] : null; + ?> + + Form->select("BlockRegion.{$i}.region", $_regions, array('value' => $selected, 'empty' => __t('--None--'))) . "\n"; ?> + Form->hidden("BlockRegion.{$i}.theme", array('value' => $theme[1])) . "\n"; ?> + Form->hidden("BlockRegion.{$i}.block_id", array('value' => $this->data['Block']['id'])) . "\n"; ?> + + + data); ?> + Form->hidden("BlockRegion.{$i}.id", array('value' => $brId[0])) . "\n"; ?> + + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Pages')); ?> + Form->input('visibility', + array( + 'type' => 'radio', + 'legend' => false, + 'separator' => '
', + 'options' => array( + 0 => __t('All pages except those listed'), + 1 => __t('Only the listed pages'), + 2 => __t('Pages on which this PHP code returns TRUE (experts only)') + ) + ) + ); + ?> + + Form->input('pages', array('type' => 'textarea', 'class' => 'plain', 'label' => false)); ?> + + + + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Roles')); ?> + Form->input('Role', array('options' => $roles, 'type' => 'select', 'selected' => Set::extract('/Role/id', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show block for specific roles'))); ?> + + Html->useTag('fieldsetend'); ?> + + Html->useTag('fieldsetstart', __t('Advanced Options')); ?> + Form->input('Block.params.class', array('value' => (isset($this->data['Block']['params']['class']) ? $this->data['Block']['params']['class'] : ''), 'label' => __t('Block class suffix'))); ?> + + Html->useTag('fieldsetend'); ?> + Html->useTag('fieldsetend'); ?> + + + Form->input(__t('Save block'), array('type' => 'submit')); ?> +Form->end(); ?> \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_index.ctp b/app/Plugin/block/View/Manage/admin_index.ctp new file mode 100644 index 00000000..8fbbb40e --- /dev/null +++ b/app/Plugin/block/View/Manage/admin_index.ctp @@ -0,0 +1,239 @@ +" . __t('clone') . " | ", + "" . __t('configure') . " | ", + "{php} return (('{Block.module}' == 'block' || {Block.clone_of} != 0) ? \"" . __t('delete') . " | \" : ''); {/php}", + "" . __t('move up') . " | ", + "" . __t('move down') . "" + ); + + $displayFields = array( + 'columns' => array( + __t('Block') => array( + 'value' => "{php} + if ('{Block.title}' == '') { + if ('{Menu.title}' != '') { + return '{Menu.title}'; + } + + return '{Block.module}_{Block.delta}'; + } + return '{Block.title}
  {BlockCustom.description}'; + {/php}", + 'tdOptions' => array('width' => '60%') + ), + __t('Region') => array( + 'value' => null + ), + __t('Actions') => array( + 'value' => implode(' ', $actions), + 'thOptions' => array('align' => 'right'), + 'tdOptions' => array('align' => 'right') + ), + ), + 'paginate' => false, + 'headerPosition' => 'top', + 'tableOptions' => array('width' => '100%') # table attributes + ); +?> + +Html->useTag('fieldsetstart', '' . $themes[Configure::read('Variable.site_theme')]['info']['name'] . ''); ?> + +Html->useTag('fieldsetend'); ?> + +
+
+ + +Html->useTag('fieldsetstart', '' . $themes[Configure::read('Variable.admin_theme')]['info']['name'] . ''); ?> + +Html->useTag('fieldsetend'); ?> + +
+
+ + +Html->useTag('fieldsetstart', '' . __t('Unassigned') . ''); ?> + +Html->useTag('fieldsetend'); ?> + + \ No newline at end of file diff --git a/app/Plugin/block/block.yaml b/app/Plugin/block/block.yaml new file mode 100644 index 00000000..2e5a0a2c --- /dev/null +++ b/app/Plugin/block/block.yaml @@ -0,0 +1,5 @@ +name: Block +description: Controls the visual building blocks a page is constructed with. Blocks are boxes of content rendered into an area, or region, of a web page. +category: Core +version: 1.0 +core: 1.x \ No newline at end of file diff --git a/app/Plugin/block/webroot/css/empty b/app/Plugin/block/webroot/css/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/webroot/img/empty b/app/Plugin/block/webroot/img/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/block/webroot/js/empty b/app/Plugin/block/webroot/js/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/comment/Config/bootstrap.php b/app/Plugin/comment/Config/bootstrap.php new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/comment/Config/routes.php b/app/Plugin/comment/Config/routes.php new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/comment/Controller/CommentAppController.php b/app/Plugin/comment/Controller/CommentAppController.php new file mode 100644 index 00000000..fc9d0bac --- /dev/null +++ b/app/Plugin/comment/Controller/CommentAppController.php @@ -0,0 +1,28 @@ + + * @link http://cms.quickapps.es + */ +class CommentAppController extends AppController { + public $uses = array('Comment.Comment'); + + public function countUnpublished() { + $count = $this->Comment->find('count', + array( + 'conditions' => array( + 'Comment.status' => 0 + ) + ) + ); + + $this->set('countUnpublished', $count); + + return $count; + } +} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/CommentController.php b/app/Plugin/comment/Controller/CommentController.php new file mode 100644 index 00000000..68cf61f3 --- /dev/null +++ b/app/Plugin/comment/Controller/CommentController.php @@ -0,0 +1,19 @@ + + * @link http://cms.quickapps.es + */ +class CommentController extends CommentAppController { + public $name = 'Comment'; + public $uses = array(); + + public function admin_index() { + $this->redirect('/admin/comment/published'); + } +} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/Component/empty b/app/Plugin/comment/Controller/Component/empty new file mode 100644 index 00000000..e69de29b diff --git a/app/Plugin/comment/Controller/PublishedController.php b/app/Plugin/comment/Controller/PublishedController.php new file mode 100644 index 00000000..de4f3dc0 --- /dev/null +++ b/app/Plugin/comment/Controller/PublishedController.php @@ -0,0 +1,57 @@ + + * @link http://cms.quickapps.es + */ +class PublishedController extends CommentAppController { + public $name = 'Published'; + public $uses = array('Comment.Comment'); + + public function admin_index() { + if (isset($this->data['Comment']['update'])) { + if (isset($this->data['Items']['id'])) { + $update = ( !in_array($this->data['Comment']['update'], array('delete'))); + + switch ($this->data['Comment']['update']) { + case 'approve': + default: + $data = array( 'field' => 'status', 'value' => 1); + break; + + case 'unapprove': + $data = array('field' => 'status', 'value' => 0); + break; + } + + foreach ($this->data['Items']['id'] as $key => $id) { + if ($update) { # update node + $this->Comment->id = $id; + $this->Comment->saveField($data['field'], $data['value'], false); + } else { # delete node + switch ($this->data['Comment']['update']) { + case 'delete': + $this->Comment->delete($id); + break; + } + } + } + } + + $this->redirect($this->referer()); + } + + $results = $this->paginate('Comment', array('Comment.status' => 1)); + + $this->countUnpublished(); + $this->set('results', $results); + $this->setCrumb('/admin/node/contents'); + $this->setCrumb( array(__t('Comments'))); + $this->title(__t('Published comments')); + } +} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/UnpublishedController.php b/app/Plugin/comment/Controller/UnpublishedController.php new file mode 100644 index 00000000..0a27c75e --- /dev/null +++ b/app/Plugin/comment/Controller/UnpublishedController.php @@ -0,0 +1,56 @@ + + * @link http://cms.quickapps.es + */ +class UnpublishedController extends CommentAppController { + public $name = 'Unpublished'; + public $uses = array('Comment.Comment'); + + public function admin_index() { + if (isset($this->data['Comment']['update'])) { + if (isset($this->data['Items']['id'])) { + $update = ( !in_array($this->data['Comment']['update'], array('delete'))); + + switch ($this->data['Comment']['update']) { + case 'approve': + default: + $data = array( 'field' => 'status', 'value' => 1); + break; + case 'unapprove': + $data = array('field' => 'status', 'value' => 0); + break; + } + + foreach ($this->data['Items']['id'] as $key => $id) { + if ($update) { # update node + $this->Comment->id = $id; + $this->Comment->saveField($data['field'], $data['value'], false); + } else { # delete node + switch ($this->data['Comment']['update']) { + case 'delete': + $this->Comment->delete($id); + break; + } + } + } + } + + $this->redirect($this->referer()); + } + + $results = $this->paginate('Comment', array('Comment.status' => 0)); + + $this->countUnpublished(); + $this->set('results', $results); + $this->setCrumb('/admin/node/contents'); + $this->setCrumb(array(__t('Comments'))); + $this->title(__t('Unpublished comments')); + } +} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/Nbbc.php b/app/Plugin/comment/Lib/Nbbc.php new file mode 100644 index 00000000..34ad214d --- /dev/null +++ b/app/Plugin/comment/Lib/Nbbc.php @@ -0,0 +1,2049 @@ + '\[', '<' => '<', '{' => '\{', '(' => '\(' ); +$regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); +$endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); +if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; +$e = $regex_endmarkers[$tagmarker]; +$b = $regex_beginmarkers[$tagmarker]; +$this->tagmarker = $tagmarker; +$this->end_tagmarker = $endmarkers[$tagmarker]; +$this->pat_main = "/( " +. "{$b}" +. "(?! -- | ' | !-- | {$b}{$b} )" +. "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" +. "{$e}" +. "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" +. "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" +. "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" +. "| -----+" +. "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" +. "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" +. "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" +. " )/Dx"; +$this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); +$this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; +$this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; +$this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; +$this->ptr = 0; +$this->unget = false; +$this->state = BBCODE_LEXSTATE_TEXT; +$this->verbatim = false; +$this->token = BBCODE_EOI; +$this->tag = false; +$this->text = ""; +} +function GuessTextLength() { +$length = 0; +$ptr = 0; +$state = BBCODE_LEXSTATE_TEXT; +while ($ptr < count($this->input)) { +$text = $this->input[$ptr++]; +if ($state == BBCODE_LEXSTATE_TEXT) { +$state = BBCODE_LEXSTATE_TAG; +$length += strlen($text); +} +else { +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$state = BBCODE_LEXSTATE_TEXT; +$length++; +break; +default: +$state = BBCODE_LEXSTATE_TEXT; +$length += strlen($text); +break; +case 40: +case 60: +case 91: +case 123: +$state = BBCODE_LEXSTATE_TEXT; +break; +} +} +} +return $length; +} +function NextToken() { +if ($this->unget) { +$this->unget = false; +return $this->token; +} +while (true) { +if ($this->ptr >= count($this->input)) { +$this->text = ""; +$this->tag = false; +return $this->token = BBCODE_EOI; +} +$this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", +$this->input[$this->ptr++]); +if ($this->verbatim) { +$this->tag = false; +if ($this->state == BBCODE_LEXSTATE_TEXT) { +$this->state = BBCODE_LEXSTATE_TAG; +$token_type = BBCODE_TEXT; +} +else { +$this->state = BBCODE_LEXSTATE_TEXT; +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$token_type = BBCODE_NL; +break; +default: +$token_type = BBCODE_WS; +break; +case 45: +case 40: +case 60: +case 91: +case 123: +$token_type = BBCODE_TEXT; +break; +} +} +if (strlen($this->text) > 0) +return $this->token = $token_type; +} +else if ($this->state == BBCODE_LEXSTATE_TEXT) { +$this->state = BBCODE_LEXSTATE_TAG; +$this->tag = false; +if (strlen($this->text) > 0) +return $this->token = BBCODE_TEXT; +} +else { +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_NL; +case 45: +if (preg_match("/^-----/", $this->text)) { +$this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_TAG; +} +else { +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +if (strlen($this->text) > 0) +return $this->token = BBCODE_TEXT; +continue; +} +default: +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_WS; +case 40: +case 60: +case 91: +case 123: +if (preg_match($this->pat_comment, $this->text)) { +$this->state = BBCODE_LEXSTATE_TEXT; +continue; +} +if (preg_match($this->pat_comment2, $this->text)) { +$this->state = BBCODE_LEXSTATE_TEXT; +continue; +} +if (preg_match($this->pat_wiki, $this->text, $matches)) { +$this->tag = Array('_name' => 'wiki', '_endtag' => false, +'_default' => @$matches[1], 'title' => @$matches[2]); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_TAG; +} +$this->tag = $this->Internal_DecodeTag($this->text); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); +} +} +} +} +function UngetToken() { +if ($this->token !== BBCODE_EOI) +$this->unget = true; +} +function PeekToken() { +$result = $this->NextToken(); +if ($this->token !== BBCODE_EOI) +$this->unget = true; +return $result; +} +function SaveState() { +return Array( +'token' => $this->token, +'text' => $this->text, +'tag' => $this->tag, +'state' => $this->state, +'input' => $this->input, +'ptr' => $this->ptr, +'unget' => $this->unget, +'verbatim' => $this->verbatim +); +} +function RestoreState($state) { +if (!is_array($state)) return; +$this->token = @$state['token']; +$this->text = @$state['text']; +$this->tag = @$state['tag']; +$this->state = @$state['state']; +$this->input = @$state['input']; +$this->ptr = @$state['ptr']; +$this->unget = @$state['unget']; +$this->verbatim = @$state['verbatim']; +} +function Internal_StripQuotes($string) { +if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) +return $matches[1]; +else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) +return $matches[1]; +else return $string; +} +function Internal_ClassifyPiece($ptr, $pieces) { +if ($ptr >= count($pieces)) return -1; +$piece = $pieces[$ptr]; +if ($piece == '=') return '='; +else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; +else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; +else return 'A'; +} +function Internal_DecodeTag($tag) { +$result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', +'_hasend' => false, '_end' => false, '_default' => false); +$tag = substr($tag, 1, strlen($tag)-2); +$ch = ord(substr($tag, 0, 1)); +if ($ch >= 0 && $ch <= 32) return $result; +$pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", +$tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); +$ptr = 0; +if (count($pieces) < 1) return $result; +if (@substr($pieces[$ptr], 0, 1) == '/') { +$result['_name'] = strtolower(substr($pieces[$ptr++], 1)); +$result['_end'] = true; +} +else { +$result['_name'] = strtolower($pieces[$ptr++]); +$result['_end'] = false; +} +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +$params = Array(); +if ($type != '=') { +$result['_default'] = false; +$params[] = Array('key' => '', 'value' => ''); +} +else { +$ptr++; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type == "\"") +$value = $this->Internal_StripQuotes($pieces[$ptr++]); +else { +$after_space = false; +$start = $ptr; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { +if ($type == ' ') $after_space = true; +if ($type == '=' && $after_space) break; +$ptr++; +} +if ($type == -1) $ptr--; +if ($type == '=') { +$ptr--; +while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') +$ptr--; +while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') +$ptr--; +} +$value = ""; +for (; $start <= $ptr; $start++) { +if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') +$value .= " "; +else $value .= $this->Internal_StripQuotes($pieces[$start]); +} +$value = trim($value); +$ptr++; +} +$result['_default'] = $value; +$params[] = Array('key' => '', 'value' => $value); +} +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { +while ($type == ' ') { +$ptr++; +$type = $this->Internal_ClassifyPiece($ptr, $pieces); +} +if ($type == 'A' || $type == '"') +$key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); +else if ($type == '=') { +$ptr++; +continue; +} +else if ($type == -1) break; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type != '=') +$value = $this->Internal_StripQuotes($key); +else { +$ptr++; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type == '"') { +$value = $this->Internal_StripQuotes($pieces[$ptr++]); +} +else if ($type != -1) { +$value = $pieces[$ptr++]; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 +&& $type != ' ') +$value .= $pieces[$ptr++]; +} +else $value = ""; +} +if (substr($key, 0, 1) != '_') +$result[$key] = $value; +$params[] = Array('key' => $key, 'value' => $value); +} +$result['_params'] = $params; +return $result; +} +} + +class BBCodeLibrary { +var $default_smileys = Array( +':)' => 'smile.gif', ':-)' => 'smile.gif', +'=)' => 'smile.gif', '=-)' => 'smile.gif', +':(' => 'frown.gif', ':-(' => 'frown.gif', +'=(' => 'frown.gif', '=-(' => 'frown.gif', +':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', +'=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', +'>:('=> 'angry.gif', '>:-('=> 'angry.gif', +'>=('=> 'angry.gif', '>=-('=> 'angry.gif', +'D:' => 'angry.gif', 'D-:' => 'angry.gif', +'D=' => 'angry.gif', 'D-=' => 'angry.gif', +'>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', +'>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', +'>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', +'>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', +'>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', +'>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', +'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', +'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', +':O' => 'surprise.gif', ':-O' => 'surprise.gif', +'=O' => 'surprise.gif', '=-O' => 'surprise.gif', +':?' => 'confuse.gif', ':-?' => 'confuse.gif', +'=?' => 'confuse.gif', '=-?' => 'confuse.gif', +':s' => 'worry.gif', ':-S' => 'worry.gif', +'=s' => 'worry.gif', '=-S' => 'worry.gif', +':|' => 'neutral.gif', ':-|' => 'neutral.gif', +'=|' => 'neutral.gif', '=-|' => 'neutral.gif', +':I' => 'neutral.gif', ':-I' => 'neutral.gif', +'=I' => 'neutral.gif', '=-I' => 'neutral.gif', +':/' => 'irritated.gif', ':-/' => 'irritated.gif', +'=/' => 'irritated.gif', '=-/' => 'irritated.gif', +':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', +'=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', +':P' => 'tongue.gif', ':-P' => 'tongue.gif', +'=P' => 'tongue.gif', '=-P' => 'tongue.gif', +'X-P' => 'tongue.gif', +'8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', +'B)' => 'cool.gif', 'B-)' => 'cool.gif', +';)' => 'wink.gif', ';-)' => 'wink.gif', +';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', +'^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', +'>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', +'<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', +'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', +';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', +':3' => 'smile3.gif', ':-3' => 'smile3.gif', +'=3' => 'smile3.gif', '=-3' => 'smile3.gif', +';3' => 'wink3.gif', ';-3' => 'wink3.gif', +'' => 'teeth.gif', '' => 'teeth.gif', +'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', +':blue:' => 'blue.gif', +':zzz:' => 'sleepy.gif', +'<3' => 'heart.gif', +':star:' => 'star.gif', +); +var $default_tag_rules = Array( +'b' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'i' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'u' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'s' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'font' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), +'method' => 'DoFont', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'color' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), +'template' => '{$_content/v}', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'size' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'allow' => Array('_default' => '/^[0-9.]+$/D'), +'method' => 'DoSize', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'sup' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'sub' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'spoiler' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'acronym' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'template' => '{$_content/v}', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'url' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoURL', +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'content' => BBCODE_REQUIRED, +'plain_start' => "", +'plain_end' => "", +'plain_content' => Array('_content', '_default'), +'plain_link' => Array('_default', '_content'), +), +'email' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoEmail', +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'content' => BBCODE_REQUIRED, +'plain_start' => "", +'plain_end' => "", +'plain_content' => Array('_content', '_default'), +'plain_link' => Array('_default', '_content'), +), +'wiki' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoWiki", +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'plain_start' => "[", +'plain_end' => "]", +'plain_content' => Array('title', '_default'), +'plain_link' => Array('_default', '_content'), +), +'img' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoImage", +'class' => 'image', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'end_tag' => BBCODE_REQUIRED, +'content' => BBCODE_REQUIRED, +'plain_start' => "[image]", +'plain_content' => Array(), +), +'rule' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoRule", +'class' => 'block', +'allow_in' => Array('listitem', 'block', 'columns'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "sns", +'after_tag' => "sns", +'plain_start' => "\n-----\n", +'plain_end' => "", +'plain_content' => Array(), +), +'br' => Array( +'mode' => BBCODE_MODE_SIMPLE, +'simple_start' => "
\n", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "s", +'after_tag' => "s", +'plain_start' => "\n", +'plain_end' => "", +'plain_content' => Array(), +), +'left' => Array( +'simple_start' => "\n
\n", +'simple_end' => "\n
\n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'right' => Array( +'simple_start' => "\n
\n", +'simple_end' => "\n
\n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'center' => Array( +'simple_start' => "\n
\n", +'simple_end' => "\n
\n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'indent' => Array( +'simple_start' => "\n
\n", +'simple_end' => "\n
\n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'columns' => Array( +'simple_start' => "\n
\n", +'simple_end' => "\n
\n", +'class' => 'columns', +'allow_in' => Array('listitem', 'block', 'columns'), +'end_tag' => BBCODE_REQUIRED, +'content' => BBCODE_REQUIRED, +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'nextcol' => Array( +'simple_start' => "\n\n", +'class' => 'nextcol', +'allow_in' => Array('columns'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "", +), +'code' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'template' => "\n
\n
Code:
\n
{\$_content/v}
\n
\n", +'class' => 'code', +'allow_in' => Array('listitem', 'block', 'columns'), +'content' => BBCODE_VERBATIM, +'before_tag' => "sns", +'after_tag' => "sn", +'before_endtag' => "sn", +'after_endtag' => "sns", +'plain_start' => "\nCode:\n", +'plain_end' => "\n", +), +'quote' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoQuote", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\nQuote:\n", +'plain_end' => "\n", +), +'list' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoList', +'class' => 'list', +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'*' => Array( +'simple_start' => "
  • ", +'simple_end' => "
  • \n", +'class' => 'listitem', +'allow_in' => Array('list'), +'end_tag' => BBCODE_OPTIONAL, +'before_tag' => "s", +'after_tag' => "s", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n * ", +'plain_end' => "\n", +), +); +function DoURL($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); +if ($bbcode->IsValidURL($url)) { +if ($bbcode->debug) +print "ISVALIDURL
    "; +if ($bbcode->url_targetable !== false && isset($params['target'])) +$target = " target=\"" . htmlspecialchars($params['target']) . "\""; +else $target = ""; +if ($bbcode->url_target !== false) +if (!($bbcode->url_targetable == 'override' && isset($params['target']))) +$target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; +return '' . $content . ''; +} +else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); +} +function DoEmail($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); +if ($bbcode->IsValidEmail($email)) +return '' . $content . ''; +else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); +} +function DoSize($bbcode, $action, $name, $default, $params, $content) { +switch ($default) { +case '0': $size = '.5em'; break; +case '1': $size = '.67em'; break; +case '2': $size = '.83em'; break; +default: +case '3': $size = '1.0em'; break; +case '4': $size = '1.17em'; break; +case '5': $size = '1.5em'; break; +case '6': $size = '2.0em'; break; +case '7': $size = '2.5em'; break; +} +return "$content"; +} +function DoFont($bbcode, $action, $name, $default, $params, $content) { +$fonts = explode(",", $default); +$result = ""; +$special_fonts = Array( +'serif' => 'serif', +'sans-serif' => 'sans-serif', +'sans serif' => 'sans-serif', +'sansserif' => 'sans-serif', +'sans' => 'sans-serif', +'cursive' => 'cursive', +'fantasy' => 'fantasy', +'monospace' => 'monospace', +'mono' => 'monospace', +); +foreach ($fonts as $font) { +$font = trim($font); +if (isset($special_fonts[$font])) { +if (strlen($result) > 0) $result .= ","; +$result .= $special_fonts[$font]; +} +else if (strlen($font) > 0) { +if (strlen($result) > 0) $result .= ","; +$result .= "'$font'"; +} +} +return "$content"; +} +function DoWiki($bbcode, $action, $name, $default, $params, $content) { +$name = $bbcode->Wikify($default); +if ($action == BBCODE_CHECK) +return strlen($name) > 0; +$title = trim(@$params['title']); +if (strlen($title) <= 0) $title = trim($default); +return "wiki_url}$name\" class=\"bbcode_wiki\">" +. htmlspecialchars($title) . ""; +} +function DoImage($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$content = trim($bbcode->UnHTMLEncode(strip_tags($content))); +if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { +if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { +if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { +$info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); +if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { +return "local_img_url}/{$content}") . "\" alt=\"" +. htmlspecialchars(basename($content)) . "\" width=\"" +. htmlspecialchars($info[0]) . "\" height=\"" +. htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; +} +} +} +else if ($bbcode->IsValidURL($content, false)) { +return "\"""; +} +} +return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); +} +function DoRule($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +else return $bbcode->rule_html; +} +function DoQuote($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +if (isset($params['name'])) { +$title = htmlspecialchars(trim($params['name'])) . " wrote"; +if (isset($params['date'])) +$title .= " on " . htmlspecialchars(trim($params['date'])); +$title .= ":"; +if (isset($params['url'])) { +$url = trim($params['url']); +if ($bbcode->IsValidURL($url)) +$title = "" . $title . ""; +} +} +else if (!is_string($default)) +$title = "Quote:"; +else $title = htmlspecialchars(trim($default)) . " wrote:"; +return "\n
    \n
    " +. $title . "
    \n
    " +. $content . "
    \n
    \n"; +} +function DoList($bbcode, $action, $name, $default, $params, $content) { +$list_styles = Array( +'1' => 'decimal', +'01' => 'decimal-leading-zero', +'i' => 'lower-roman', +'I' => 'upper-roman', +'a' => 'lower-alpha', +'A' => 'upper-alpha', +); +$ci_list_styles = Array( +'circle' => 'circle', +'disc' => 'disc', +'square' => 'square', +'greek' => 'lower-greek', +'armenian' => 'armenian', +'georgian' => 'georgian', +); +$ul_types = Array( +'circle' => 'circle', +'disc' => 'disc', +'square' => 'square', +); +$default = trim($default); +if ($action == BBCODE_CHECK) { +if (!is_string($default) || strlen($default) == "") return true; +else if (isset($list_styles[$default])) return true; +else if (isset($ci_list_styles[strtolower($default)])) return true; +else return false; +} +if (!is_string($default) || strlen($default) == "") { +$elem = 'ul'; +$type = ''; +} +else if ($default == '1') { +$elem = 'ol'; +$type = ''; +} +else if (isset($list_styles[$default])) { +$elem = 'ol'; +$type = $list_styles[$default]; +} +else { +$default = strtolower($default); +if (isset($ul_types[$default])) { +$elem = 'ul'; +$type = $ul_types[$default]; +} +else if (isset($ci_list_styles[$default])) { +$elem = 'ol'; +$type = $ci_list_styles[$default]; +} +} +if (strlen($type)) +return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content\n"; +else return "\n<$elem class=\"bbcode_list\">\n$content\n"; +} +} + +class BBCodeEmailAddressValidator { +function check_email_address($strEmailAddress) { +if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { +return false; +} +$intAtSymbol = strrpos($strEmailAddress, '@'); +if ($intAtSymbol === false) { +return false; +} +$arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); +$arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); +$arrTempAddress[0] = preg_replace('/"[^"]+"/' +,'' +,$arrEmailAddress[0]); +$arrTempAddress[1] = $arrEmailAddress[1]; +$strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; +if (strrpos($strTempAddress, '@') !== false) { +return false; +} +if (!$this->check_local_portion($arrEmailAddress[0])) { +return false; +} +if (!$this->check_domain_portion($arrEmailAddress[1])) { +return false; +} +return true; +} +function check_local_portion($strLocalPortion) { +if (!$this->check_text_length($strLocalPortion, 1, 64)) { +return false; +} +$arrLocalPortion = explode('.', $strLocalPortion); +for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { +if (!preg_match('.^(' +. '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' +. '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' +.'|' +. '("[^\\\"]{0,62}")' +.')$.' +,$arrLocalPortion[$i])) { +return false; +} +} +return true; +} +function check_domain_portion($strDomainPortion) { +if (!$this->check_text_length($strDomainPortion, 1, 255)) { +return false; +} +if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' +.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' +,$strDomainPortion) || +preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' +.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' +,$strDomainPortion)) { +return true; +} else { +$arrDomainPortion = explode('.', $strDomainPortion); +if (sizeof($arrDomainPortion) < 2) { +return false; +} +for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { +if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { +return false; +} +if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' +.'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { +return false; +} +} +} +return true; +} +function check_text_length($strText, $intMinimum, $intMaximum) { +$intTextLength = strlen($strText); +if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { +return false; +} else { +return true; +} +} +} + +class BBCode { +var $tag_rules; +var $defaults; +var $current_class; +var $root_class; +var $lost_start_tags; +var $start_tags; +var $allow_ampersand; +var $tag_marker; +var $ignore_newlines; +var $plain_mode; +var $detect_urls; +var $url_pattern; +var $output_limit; +var $text_length; +var $was_limited; +var $limit_tail; +var $limit_precision; +var $smiley_dir; +var $smiley_url; +var $smileys; +var $smiley_regex; +var $enable_smileys; +var $wiki_url; +var $local_img_dir; +var $local_img_url; +var $url_targetable; +var $url_target; +var $rule_html; +var $pre_trim; +var $post_trim; +var $debug; +function BBCode() { +$this->defaults = new BBCodeLibrary; +$this->tag_rules = $this->defaults->default_tag_rules; +$this->smileys = $this->defaults->default_smileys; +$this->enable_smileys = true; +$this->smiley_regex = false; +$this->smiley_dir = $this->GetDefaultSmileyDir(); +$this->smiley_url = $this->GetDefaultSmileyURL(); +$this->wiki_url = $this->GetDefaultWikiURL(); +$this->local_img_dir = $this->GetDefaultLocalImgDir(); +$this->local_img_url = $this->GetDefaultLocalImgURL(); +$this->rule_html = $this->GetDefaultRuleHTML(); +$this->pre_trim = ""; +$this->post_trim = ""; +$this->root_class = 'block'; +$this->lost_start_tags = Array(); +$this->start_tags = Array(); +$this->tag_marker = '['; +$this->allow_ampsersand = false; +$this->current_class = $this->root_class; +$this->debug = false; +$this->ignore_newlines = false; +$this->output_limit = 0; +$this->plain_mode = false; +$this->was_limited = false; +$this->limit_tail = "..."; +$this->limit_precision = 0.15; +$this->detect_urls = false; +$this->url_pattern = '{$text/h}'; +$this->url_targetable = false; +$this->url_target = false; +} +function SetPreTrim($trim = "a") { $this->pre_trim = $trim; } +function GetPreTrim() { return $this->pre_trim; } +function SetPostTrim($trim = "a") { $this->post_trim = $trim; } +function GetPostTrim() { return $this->post_trim; } +function SetRoot($class = 'block') { $this->root_class = $class; } +function SetRootInline() { $this->root_class = 'inline'; } +function SetRootBlock() { $this->root_class = 'block'; } +function GetRoot() { return $this->root_class; } +function SetDebug($enable = true) { $this->debug = $enable; } +function GetDebug() { return $this->debug; } +function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; } +function GetAllowAmpersand() { return $this->allow_ampersand; } +function SetTagMarker($marker = '[') { $this->tag_marker = $marker; } +function GetTagMarker() { return $this->tag_marker; } +function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; } +function GetIgnoreNewlines() { return $this->ignore_newlines; } +function SetLimit($limit = 0) { $this->output_limit = $limit; } +function GetLimit() { return $this->output_limit; } +function SetLimitTail($tail = "...") { $this->limit_tail = $tail; } +function GetLimitTail() { return $this->limit_tail; } +function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; } +function GetLimitPrecision() { return $this->limit_precision; } +function WasLimited() { return $this->was_limited; } +function SetPlainMode($enable = true) { $this->plain_mode = $enable; } +function GetPlainMode() { return $this->plain_mode; } +function SetDetectURLs($enable = true) { $this->detect_urls = $enable; } +function GetDetectURLs() { return $this->detect_urls; } +function SetURLPattern($pattern) { $this->url_pattern = $pattern; } +function GetURLPattern() { return $this->url_pattern; } +function SetURLTargetable($enable) { $this->url_targetable = $enable; } +function GetURLTargetable() { return $this->url_targetable; } +function SetURLTarget($target) { $this->url_target = $target; } +function GetURLTarget() { return $this->url_target; } +function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; } +function RemoveRule($name) { unset($this->tag_rules[$name]); } +function GetRule($name) { return isset($this->tag_rules[$name]) +? $this->tag_rules[$name] : false; } +function ClearRules() { $this->tag_rules = Array(); } +function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name]) +? $this->defaults->default_tag_rules[$name] : false; } +function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name])) +$this->AddRule($name, $this->defaults->default_tag_rules[$name]); +else $this->RemoveRule($name); } +function GetDefaultRules() { return $this->defaults->default_tag_rules; } +function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; } +function SetWikiURL($url) { $this->wiki_url = $url; } +function GetWikiURL($url) { return $this->wiki_url; } +function GetDefaultWikiURL() { return '/?page='; } +function SetLocalImgDir($path) { $this->local_img_dir = $path; } +function GetLocalImgDir() { return $this->local_img_dir; } +function GetDefaultLocalImgDir() { return "img"; } +function SetLocalImgURL($path) { $this->local_img_url = $path; } +function GetLocalImgURL() { return $this->local_img_url; } +function GetDefaultLocalImgURL() { return "img"; } +function SetRuleHTML($html) { $this->rule_html = $html; } +function GetRuleHTML() { return $this->rule_html; } +function GetDefaultRuleHTML() { return "\n
    \n"; } +function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; } +function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; } +function GetSmiley($code) { return isset($this->smileys[$code]) +? $this->smileys[$code] : false; } +function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; } +function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code]) +? $this->defaults->default_smileys[$code] : false; } +function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code]; +$this->smiley_regex = false; } +function GetDefaultSmileys() { return $this->defaults->default_smileys; } +function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys; +$this->smiley_regex = false; } +function SetSmileyDir($path) { $this->smiley_dir = $path; } +function GetSmileyDir() { return $this->smiley_dir; } +function GetDefaultSmileyDir() { return "smileys"; } +function SetSmileyURL($path) { $this->smiley_url = $path; } +function GetSmileyURL() { return $this->smiley_url; } +function GetDefaultSmileyURL() { return "smileys"; } +function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; } +function GetEnableSmileys() { return $this->enable_smileys; } +function nl2br($string) { +return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "
    \n", $string); +} +function UnHTMLEncode($string) { +if (function_exists("html_entity_decode")) +return html_entity_decode($string); +$string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); +$string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string); +$trans_tbl = get_html_translation_table(HTML_ENTITIES); +$trans_tbl = array_flip($trans_tbl); +return strtr($string, $trans_tbl); +} +function Wikify($string) { +return rawurlencode(str_replace(" ", "_", +trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string)))); +} +function IsValidURL($string, $email_too = true) { +if (preg_match("/^ +(?:https?|ftp):\\/\\/ +(?: +(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+ +[a-zA-Z0-9] +(?:[a-zA-Z0-9-]*[a-zA-Z0-9])? +| +\\[ +(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} +(?: +25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]: +(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ +) +\\] +) +(?::[0-9]{1,5})? +(?:[\\/\\?\\#][^\\n\\r]*)? +$/Dx", $string)) return true; +if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) +return true; +if ($email_too) +if (substr($string, 0, 7) == "mailto:") +return $this->IsValidEmail(substr($string, 7)); +return false; +} +function IsValidEmail($string) { +$validator = new BBCodeEmailAddressValidator; +return $validator->check_email_address($string); +/* +return preg_match("/^ +(?: +[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+ +(?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)* +| +\"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\" +) +@ +(?: +(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ +[a-z0-9] +(?:[a-z0-9-]*[a-z0-9])? +| +\\[ +(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} +(?: +25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: +(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ +) +\\] +) +$/Dx", $string); +*/ +} +function HTMLEncode($string) { +if (!$this->allow_ampersand) +return htmlspecialchars($string); +else return str_replace(Array('<', '>', '"'), +Array('<', '>', '"'), $string); +} +function FixupOutput($string) { +if (!$this->detect_urls) { +$output = $this->Internal_ProcessSmileys($string); +} +else { +$chunks = $this->Internal_AutoDetectURLs($string); +$output = Array(); +if (count($chunks)) { +$is_a_url = false; +foreach ($chunks as $index => $chunk) { +if (!$is_a_url) { +$chunk = $this->Internal_ProcessSmileys($chunk); +} +$output[] = $chunk; +$is_a_url = !$is_a_url; +} +} +$output = implode("", $output); +} +return $output; +} +function Internal_ProcessSmileys($string) { +if (!$this->enable_smileys || $this->plain_mode) { +$output = $this->HTMLEncode($string); +} +else { +if ($this->smiley_regex === false) { +$this->Internal_RebuildSmileys(); +} +$tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE); +if (count($tokens) <= 1) { +$output = $this->HTMLEncode($string); +} +else { +$output = ""; +$is_a_smiley = false; +foreach ($tokens as $token) { +if (!$is_a_smiley) { +$output .= $this->HTMLEncode($token); +} +else { +if (isset($this->smiley_info[$token])) { +$info = $this->smiley_info[$token]; +} +else { +$info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]); +$this->smiley_info[$token] = $info; +} +$alt = htmlspecialchars($token); +$output .= "smiley_url . '/' . $this->smileys[$token]) +. "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" +. " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />"; +} +$is_a_smiley = !$is_a_smiley; +} +} +} +return $output; +} +function Internal_RebuildSmileys() { +$regex = Array("/(?smileys as $code => $filename) { +if (!$first) $regex[] = "|"; +$regex[] = preg_quote("$code", '/'); +$first = false; +} +$regex[] = ")(?![\\w])/"; +$this->smiley_regex = implode("", $regex); +} +function Internal_AutoDetectURLs($string) { +$output = preg_split("/( (?: +(?:https?|ftp) : \\/* +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} ) +) +(?: : [0-9]+ )? +(?! [a-zA-Z0-9.:-] ) +(?: +\\/ +[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* +)? +(?: +[?#] +[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ +)? +) | (?: +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +) +(?: : [0-9]+ )? +(?! [a-zA-Z0-9.:-] ) +(?: +\\/ +[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* +)? +(?: +[?#] +[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ +)? +) | (?: +[a-zA-Z0-9._-]{2,} @ +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +) +))/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE); +if (count($output) > 1) { +$is_a_url = false; +foreach ($output as $index => $token) { +if ($is_a_url) { +if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) { +$url = "mailto:" . $token; +} +else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) { +$url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3]; +} +else { +preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches); +$url = "http:/" . "/" . $matches[1] . "/" . $matches[2]; +} +$params = @parse_url($url); +if (!is_array($params)) $params = Array(); +$params['url'] = $url; +$params['link'] = $url; +$params['text'] = $token; +$output[$index] = $this->FillTemplate($this->url_pattern, $params); +} +$is_a_url = !$is_a_url; +} +} +return $output; +} +function FillTemplate($template, $insert_array, $default_array = Array()) { +$pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template, +-1, PREG_SPLIT_DELIM_CAPTURE); +if (count($pieces) <= 1) +return $template; +$result = Array(); +$is_an_insert = false; +foreach ($pieces as $piece) { +if (!$is_an_insert) { +$result[] = $piece; +} +else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) { +$result[] = $piece; +} +else { +if (isset($insert_array[$matches[1]])) +$value = @$insert_array[$matches[1]]; +else $value = @$default_array[$matches[1]]; +if (strlen(@$matches[2])) { +foreach (split(".", substr($matches[2], 1)) as $index) { +if (is_array($value)) +$value = @$value[$index]; +else if (is_object($value)) { +$value = (array)$value; +$value = @$value[$index]; +} +else $value = ""; +} +} +switch (gettype($value)) { +case 'boolean': $value = $value ? "true" : "false"; break; +case 'integer': $value = (string)$value; break; +case 'double': $value = (string)$value; break; +case 'string': break; +default: $value = ""; break; +} +if (strlen(@$matches[3])) +$flags = array_flip(str_split($matches[3])); +else $flags = Array(); +if (!isset($flags['v'])) { +if (isset($flags['w'])) +$value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value); +if (isset($flags['t'])) $value = trim($value); +if (isset($flags['b'])) $value = basename($value); +if (isset($flags['e'])) $value = $this->HTMLEncode($value); +else if (isset($flags['k'])) $value = $this->Wikify($value); +else if (isset($flags['h'])) $value = htmlspecialchars($value); +else if (isset($flags['u'])) $value = urlencode($value); +if (isset($flags['n'])) $value = $this->nl2br($value); +} +$result[] = $value; +} +$is_an_insert = !$is_an_insert; +} +return implode("", $result); +} +function Internal_CollectText($array, $start = 0) { +ob_start(); +for ($start = intval($start), $end = count($array); $start < $end; $start++) +print $array[$start][BBCODE_STACK_TEXT]; +$output = ob_get_contents(); +ob_end_clean(); +return $output; +} +function Internal_CollectTextReverse($array, $start = 0, $end = 0) { +ob_start(); +for ($start = intval($start); $start >= $end; $start--) +print $array[$start][BBCODE_STACK_TEXT]; +$output = ob_get_contents(); +ob_end_clean(); +return $output; +} +function Internal_GenerateOutput($pos) { +$output = Array(); +while (count($this->stack) > $pos) { +$token = array_pop($this->stack); +if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) { +$output[] = $token; +} +else { +$name = @$token[BBCODE_STACK_TAG]['_name']; +$rule = @$this->tag_rules[$name]; +$end_tag = @$rule['end_tag']; +if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED; +else $end_tag = $rule['end_tag']; +array_pop($this->start_tags[$name]); +if ($end_tag == BBCODE_PROHIBIT) { +$output[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TAG => false, +BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT], +BBCODE_STACK_CLASS => $this->current_class, +); +} +else { +if ($end_tag == BBCODE_REQUIRED) +@$this->lost_start_tags[$name] += 1; +$end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output); +$this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output); +$tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end); +$this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack); +$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]); +$tag_output = $this->DoTag(BBCODE_OUTPUT, $name, +@$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body); +$output = Array(Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TAG => false, +BBCODE_STACK_TEXT => $tag_output, +BBCODE_STACK_CLASS => $this->current_class +)); +} +} +} +$this->Internal_ComputeCurrentClass(); +return $output; +} +function Internal_RewindToClass($class_list) { +$pos = count($this->stack) - 1; +while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) +$pos--; +if ($pos < 0) { +if (!in_array($this->root_class, $class_list)) +return false; +} +$output = $this->Internal_GenerateOutput($pos+1); +while (count($output)) { +$token = array_pop($output); +$token[BBCODE_STACK_CLASS] = $this->current_class; +$this->stack[] = $token; +} +return true; +} +function Internal_FinishTag($tag_name) { +if (strlen($tag_name) <= 0) +return false; +if (isset($this->start_tags[$tag_name]) +&& count($this->start_tags[$tag_name])) +$pos = array_pop($this->start_tags[$tag_name]); +else $pos = -1; +if ($pos < 0) return false; +$newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'], +$pos+1, $this->stack); +$delta = $newpos - ($pos+1); +$output = $this->Internal_GenerateOutput($newpos); +$newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'], +0, $output); +$output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend); +while ($delta-- > 0) +array_pop($this->stack); +$this->Internal_ComputeCurrentClass(); +return $output; +} +function Internal_ComputeCurrentClass() { +if (count($this->stack) > 0) +$this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS]; +else $this->current_class = $this->root_class; +} +function Internal_DumpStack($array = false, $raw = false) { +if (!$raw) $string = ""; +else $string = ""; +if ($array === false) +$array = $this->stack; +foreach ($array as $item) { +switch (@$item[BBCODE_STACK_TOKEN]) { +case BBCODE_TEXT: +$string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" "; +break; +case BBCODE_WS: +$string .= "WS "; +break; +case BBCODE_NL: +$string .= "NL "; +break; +case BBCODE_TAG: +$string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] "; +break; +default: +$string .= "unknown "; +break; +} +} +if (!$raw) $string .= ""; +return $string; +} +function Internal_CleanupWSByPoppingStack($pattern, &$array) { +if (strlen($pattern) <= 0) return; +$oldlen = count($array); +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS) +array_pop($array); +break; +case 'n': +if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL) +array_pop($array); +break; +case 'a': +while (count($array) > 0 +&& (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS +|| $token == BBCODE_NL)) +array_pop($array); +break; +} +} +if (count($array) != $oldlen) { +$this->Internal_ComputeCurrentClass(); +} +} +function Internal_CleanupWSByEatingInput($pattern) { +if (strlen($pattern) <= 0) return; +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +$token_type = $this->lexer->NextToken(); +while ($token_type == BBCODE_WS) { +$token_type = $this->lexer->NextToken(); +} +$this->lexer->UngetToken(); +break; +case 'n': +$token_type = $this->lexer->NextToken(); +if ($token_type != BBCODE_NL) +$this->lexer->UngetToken(); +break; +case 'a': +$token_type = $this->lexer->NextToken(); +while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) { +$token_type = $this->lexer->NextToken(); +} +$this->lexer->UngetToken(); +break; +} +} +} +function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) { +if (strlen($pattern) <= 0) return $pos; +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) +$pos++; +break; +case 'n': +if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) +$pos++; +break; +case 'a': +while ($pos < count($array) +&& (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) +$pos++; +break; +} +} +return $pos; +} +function Internal_LimitText($string, $limit) { +$chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE); +$output = ""; +foreach ($chunks as $chunk) { +if (strlen($output) + strlen($chunk) > $limit) +break; +$output .= $chunk; +} +$output = rtrim($output); +return $output; +} +function Internal_DoLimit() { +$this->Internal_CleanupWSByPoppingStack("a", $this->stack); +if (strlen($this->limit_tail) > 0) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->limit_tail, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->was_limited = true; +} +function DoTag($action, $tag_name, $default_value, $params, $contents) { +$tag_rule = @$this->tag_rules[$tag_name]; +switch ($action) { +case BBCODE_CHECK: +if (isset($tag_rule['allow'])) { +foreach ($tag_rule['allow'] as $param => $pattern) { +if ($param == '_content') $value = $contents; +else if ($param == '_defaultcontent') { +if (strlen($default_value)) +$value = $default_value; +else $value = $contents; +} +else { +if (isset($params[$param])) +$value = $params[$param]; +else $value = @$tag_rule['default'][$param]; +} +if (!preg_match($pattern, $value)) { +return false; +} +} +return true; +} +switch (@$tag_rule['mode']) { +default: +case BBCODE_MODE_SIMPLE: +$result = true; +break; +case BBCODE_MODE_ENHANCED: +$result = true; +break; +case BBCODE_MODE_INTERNAL: +$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_LIBRARY: +$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_CALLBACK: +$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +} +return $result; +case BBCODE_OUTPUT: +if ($this->plain_mode) { +if (!isset($tag_rule['plain_content'])) +$plain_content = Array('_content'); +else $plain_content = $tag_rule['plain_content']; +$result = $possible_content = ""; +foreach ($plain_content as $possible_content) { +if ($possible_content == '_content' +&& strlen($contents) > 0) { +$result = $contents; +break; +} +if (isset($params[$possible_content]) +&& strlen($params[$possible_content]) > 0) { +$result = htmlspecialchars($params[$possible_content]); +break; +} +} +$start = @$tag_rule['plain_start']; +$end = @$tag_rule['plain_end']; +if (isset($tag_rule['plain_link'])) { +$link = $possible_content = ""; +foreach ($tag_rule['plain_link'] as $possible_content) { +if ($possible_content == '_content' +&& strlen($contents) > 0) { +$link = $this->UnHTMLEncode(strip_tags($contents)); +break; +} +if (isset($params[$possible_content]) +&& strlen($params[$possible_content]) > 0) { +$link = $params[$possible_content]; +break; +} +} +$params = @parse_url($link); +if (!is_array($params)) $params = Array(); +$params['link'] = $link; +$params['url'] = $link; +$start = $this->FillTemplate($start, $params); +$end = $this->FillTemplate($end, $params); +} +return $start . $result . $end; +} +switch (@$tag_rule['mode']) { +default: +case BBCODE_MODE_SIMPLE: +$result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end']; +break; +case BBCODE_MODE_ENHANCED: +$result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents); +break; +case BBCODE_MODE_INTERNAL: +$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_LIBRARY: +$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_CALLBACK: +$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +} +return $result; +default: +return false; +} +} +function Internal_DoEnhancedTag($tag_rule, $params, $contents) { +$params['_content'] = $contents; +$params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents; +return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']); +} +function Internal_UpdateParamsForMissingEndTag(&$params) { +switch ($this->tag_marker) { +case '[': $tail_marker = ']'; break; +case '<': $tail_marker = '>'; break; +case '{': $tail_marker = '}'; break; +case '(': $tail_marker = ')'; break; +default: $tail_marker = $this->tag_marker; break; +} +$params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker; +} +function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) { +if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, ""); +$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) { +$state = $this->lexer->SaveState(); +$end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker; +$start = count($this->stack); +$this->lexer->verbatim = true; +while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) { +if ($this->lexer->text == $end_tag) { +$end_tag_params = $this->lexer->tag; +break; +} +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$text = $this->Internal_LimitText($this->lexer->text, +$this->output_limit - $this->text_length); +if (strlen($text) > 0) { +$this->text_length += strlen($text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->Internal_DoLimit(); +break; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => $token_type, +BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text), +BBCODE_STACK_TAG => $this->lexer->tag, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->lexer->verbatim = false; +if ($token_type == BBCODE_EOI) { +$this->lexer->RestoreState($state); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack); +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack); +$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']); +$content = $this->Internal_CollectText($this->stack, $newstart); +array_splice($this->stack, $start); +$this->Internal_ComputeCurrentClass(); +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); +$tag_params['_endtag'] = $end_tag_params['_tag']; +$tag_params['_hasend'] = true; +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, +@$tag_params['_default'], $tag_params, $content); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Internal_ParseStartTagToken() { +$tag_params = $this->lexer->tag; +$tag_name = @$tag_params['_name']; +if (!isset($this->tag_rules[$tag_name])) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$tag_rule = $this->tag_rules[$tag_name]; +$allow_in = is_array($tag_rule['allow_in']) +? $tag_rule['allow_in'] : Array($this->root_class); +if (!in_array($this->current_class, $allow_in)) { +if (!$this->Internal_RewindToClass($allow_in)) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +} +$end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED; +if ($end_tag == BBCODE_PROHIBIT) { +$this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule); +return; +} +if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +if (@$tag_rule['content'] == BBCODE_VERBATIM) { +$this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule); +return; +} +if (isset($tag_rule['class'])) +$newclass = $tag_rule['class']; +else $newclass = $this->root_class; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => $this->lexer->token, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => $this->lexer->tag, +BBCODE_STACK_CLASS => ($this->current_class = $newclass), +); +if (!isset($this->start_tags[$tag_name])) +$this->start_tags[$tag_name] = Array(count($this->stack)-1); +else $this->start_tags[$tag_name][] = count($this->stack)-1; +} +function Internal_ParseEndTagToken() { +$tag_params = $this->lexer->tag; +$tag_name = @$tag_params['_name']; +$contents = $this->Internal_FinishTag($tag_name); +if ($contents === false) { +if (@$this->lost_start_tags[$tag_name] > 0) { +$this->lost_start_tags[$tag_name]--; +} +else { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +return; +} +$start_tag_node = array_pop($this->stack); +$start_tag_params = $start_tag_node[BBCODE_STACK_TAG]; +$this->Internal_ComputeCurrentClass(); +$this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack); +$start_tag_params['_endtag'] = $tag_params['_tag']; +$start_tag_params['_hasend'] = true; +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'], +$start_tag_params, $contents); +$this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Parse($string) { +$this->lexer = new BBCodeLexer($string, $this->tag_marker); +$this->lexer->debug = $this->debug; +$old_output_limit = $this->output_limit; +if ($this->output_limit > 0) { +if (strlen($string) < $this->output_limit) { +$this->output_limit = 0; +} +else if ($this->limit_precision > 0) { +$guess_length = $this->lexer->GuessTextLength(); +if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) { +$this->output_limit = 0; +} +else { +} +} +} +$this->stack = Array(); +$this->start_tags = Array(); +$this->lost_start_tags = Array(); +$this->text_length = 0; +$this->was_limited = false; +if (strlen($this->pre_trim) > 0) +$this->Internal_CleanupWSByEatingInput($this->pre_trim); +$newline = $this->plain_mode ? "\n" : "
    \n"; +while (true) { +if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) { +break; +} +switch ($token_type) { +case BBCODE_TEXT: +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$text = $this->Internal_LimitText($this->lexer->text, +$this->output_limit - $this->text_length); +if (strlen($text) > 0) { +$this->text_length += strlen($text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +break; +case BBCODE_WS: +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_WS, +BBCODE_STACK_TEXT => $this->lexer->text, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +break; +case BBCODE_NL: +if ($this->ignore_newlines) { +if ($this->output_limit > 0 +&& $this->text_length + 1 >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += 1; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_WS, +BBCODE_STACK_TEXT => "\n", +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +else { +$this->Internal_CleanupWSByPoppingStack("s", $this->stack); +if ($this->output_limit > 0 +&& $this->text_length + 1 >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += 1; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_NL, +BBCODE_STACK_TEXT => $newline, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +$this->Internal_CleanupWSByEatingInput("s"); +} +break; +case BBCODE_TAG: +$this->Internal_ParseStartTagToken(); +break; +case BBCODE_ENDTAG: +$this->Internal_ParseEndTagToken(); +break; +default: +break; +} +} +if (strlen($this->post_trim) > 0) +$this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack); +$result = $this->Internal_GenerateOutput(0); +$result = $this->Internal_CollectTextReverse($result, count($result) - 1); +$this->output_limit = $old_output_limit; +if ($this->plain_mode) { +$result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result); +$result = preg_replace("/(?:[\\x20]*\\n) {2,}[\\x20]*/", "\n\n", $result); +$result = trim($result); +} +return $result; +} +} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/CHANGELOG b/app/Plugin/comment/Lib/nbbc/CHANGELOG new file mode 100644 index 00000000..3c25684c --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/CHANGELOG @@ -0,0 +1,185 @@ + +2010-09-17 / v1.4.5 / Phantom Inker (inker2576@yahoo.com) + * SECURITY FIX: Fixed a bug that would allow arbitrary HTML + injection via the standard [acronym] tag. + +2010-08-16 / non-released / Phantom Inker (inker2576@yahoo.com) + * Added another unit test for unclosed lists. + +2010-06-05 / v1.4.4 / Phantom Inker (inker2576@yahoo.com) + * Fixed the long-standing bug in verbatim parsing so that + interior content is now truly verbatim and unprocessed. + This allows source code to be correctly represented inside + a [code] block even if it contains things like ['foo']. + * Fixed typos (missing $this-> and wrong variable names) in + SetDefaultRule() and SetWikiURL() that caused them to + reference things that didn't exist. + * Fixed a typo (missing backslash) in IsValidURL() that caused + it to miss some legal URLs. + * Fixed a bug that was incorrectly generating lists in some cases. + +2009-10-31 / non-released / Phantom Inker (inker2576@yahoo.com) + * Fixed a bug in SetWikiURL that referenced an invalid function + parameter. + * Fixed a bug in the lexer that was causing comments to produce + an invalid following state (and thus causing comments to + effectively turn off tag parsing thereafter!). + * Added tests for the comment bug, and for a reported (but not + yet reproduced) quote bug. + +2009-10-10 / v1.4.3 / Phantom Inker (inker2576@yahoo.com) + * Fixed a bug in plain mode that was causing tags processed in + plain mode to throw errors (it was calling a function that + had since been renamed). + * Added tests for plain mode to ensure this omission won't + happen again. + +2009-06-21 / v1.4.2 / Phantom Inker (inker2576@yahoo.com) + * SECURITY FIX: [img] tag was allowing HTML content to be passed + through. + +2009-04-03 / v1.4.1 / Phantom Inker (inker2576@yahoo.com) + * Fixed documentation for [quote] tag's url= attribute. + +2009-03-20 / v1.4.1 / Phantom Inker (inker2576@yahoo.com) + * Added support for SetURLTarget() and GetURLTarget(), and + extended support for SetURLTargeting(). + * Added four more regression tests for URL targeting. + +2009-02-16 / v1.4.0 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Replaced parsing logic for tags so that an equal-sign before + whitespace can be considered part of a tag's value. This + allows [url=http://foo.com?bar=baz] to be parsed the way the + user expects. Note that this does break some (probably + invalid) tags, but overall, the new parsing algorithm seems + to be a win. + * Added several regression tests for the new tag-parsing logic. + * Added SetURLTargetable() and GetURLTargetable(). However, the + default setting is to have this disabled for security reasons. + * Updated [url] tag to support URL targeting, using the standard + target="" form. + * Fixed Wikify() so that wiki links with ' or , in them will be + processed correctly. + * Added the _tag, _endtag, _hasend, and _params tag parameters. + * Added new appendix H for the tag parameters; added additional + documentation for Set/GetURLTargetable(). + * Added $debug flag to BBCodeLexer, replicated from the same flag in + class BBCode itself. + +2009-01-08 / v1.3.4 / Phantom Inker (inker2576@yahoo.com) + * Replaced IsValidEmail() with e-mail validator from AddedBytes.com, + which does a considerably better job of validating incorrect + e-mails. This fixes a bug were some e-mail addresses in [email] + tags were being rejected even though they were legal. + +2008-12-01 / v1.3.3 / Phantom Inker (inker2576@yahoo.com) + * Added regression tests for [[wiki]] tags due to possible issue + identified in debugging CMXpress v0.9.8. + +2008-11-06 / v1.3.2 / Phantom Inker (inker2576@yahoo.com) + * Added regression tests for parsing equal signs in tags per tracker + ID #2220598. + +2008-08-08 / v1.3.1 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Added profiler class and profiling code. + * Added support for removable sections in compressed version. + * Removed major performance bottleneck in Internal_ProcessSmileys() + that cut overall parsing times to about one third of what they + were before. + * Added PHP 4 compatiblity check for str_split; fixes a significant + bug on PHP 4 systems. + * Simplified test script's time output, since the new profiling code can + track it more accurately when debugging. + +2008-08-07 / v1.3 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Exposed the functionality of FillTemplate() as a public function. + * Changed template-filling to have its default encoding be RAW, not + HTMLEncode(). This will affect any ENHANCED rule used with v1.2 + or earlier; those rules MUST be converted to use the new /e + flag or they will not function properly. + * Changed 'plain_link' processing to use FillTemplate(). + * Added URL/domain-name/email-address auto-detection (turned off by default). + * Added conformance tests for URL/etc. auto-detection. + * Added URL/etc. auto-detection APIs to documentation. + * Added 'k' flag and '.' operator to FillTemplate(). + * Added FillTemplate() API to documentation. + * Added FillTemplate() example. + * Changed example programs to use "nbbc.php", not "src/nbbc_main.php". + * Added RSS example to demonstrate limiting and plain modes. + * Changed full example to offer checkboxes for testing various modes. + * Added an appendix containing sample CSS. + * Added usage sections to the documentation for limited-length mode, + plain-HTML mode, and URL-autodetection mode. + * Added new NBBC logo :-) + Stats: 127 KB source, 260 KB user's manual, 131 conformance tests [ALL PASS]. + +2008-08-04 / v1.2 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Added output-limiting mode. + * Added output-limit tails. + * Added fuzzy output limiting. + * Added plain-text mode. + * Added API documentation for output-limit and plain-text mode. + * Changed examples to use XHTML header declarations + * Added limiting example. + * Added strip_tags() calls to [url], [email], and [img] tags for safety. + * Fixed double-encoding bug in [url], [email], and [img] tags when specifying + the URL as content instead of in the tag itself. + * Fixed double-encoding bug in the [color] and [acronym] tags. + +2008-07-30 / v1.1 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Added more conformance tests and expanded some existing ones. + * Added performance-measuring to test_nbbc.php. + * Optimized lexer pattern to return fewer text tokens whenever possible. + * Optimized stack to use symbolic constants instead of strings. + * Optimized whitespace removal to perform fewer lexer calls. + * Optimized parser core when dealing with text/whitespace/newlines. + * Optimized text-collection to use output buffering instead of string cats. + * Optimized smiley conversions for the most common case. + * Several other small optimizations and cleanups. + * Overall performance improvement of about 30%. + * Fixed incorrect rule declaration logic for callback functions. + * Fixed section-link glitch in user's manual. + * Fixed bug that was prohibiting uncompressed version from working right. + * Removed (currently) unused BBCODE_OUTPUT_TEXT declarations (these probably + need to be added in for real at a later date). + * Added sample CSS appendix to user's manual. + * Added short table-of-contents to user's manual for web site. + * Added five example scripts to the new examples/ directory. + Stats: 105 KB source, 217 KB user's manual, 121 conformance tests [ALL PASS]. + +2008-07-29 / v1.0 RELEASE / Phantom Inker (inker2576@yahoo.com) + * Added newline-ignore mode and conformance tests for it. + * Finished writing first-edition user's manual. + Stats: 102 KB source, 212 KB user's manual, 120 conformance tests [ALL PASS]. + +2008-07-24 / v1.0 RC5 / Phantom Inker (inker2576@yahoo.com) + * Added alternate tag-marker modes and conformance tests for it. + * Added ampersand pass-through mode and conformance tests for it. + * Fixed HTML-encoding error in BBCODE_VERBATIM tags; converted [code] + tag to use direct htmlspecialchars() calls. + +2008-07-23 / v1.0 RC4 / Phantom Inker (inker2576@yahoo.com) + * Updated tests to recognize new CSS classes in standard library's output. + * Fixed class-output bugs in standard library. + +2008-07-21 / v1.0 RC3 / Phantom Inker (inker2576@yahoo.com) + * Converted user's manual to new auto-generating format, and wrote + additional documentation. User's manual is now about 90% complete. + * Reworked directory structure to be cleaner, and added support for + generating compacted "nbbc.php" version. + +2008-07-16 / v1.0 RC2 / Phantom Inker (inker2576@yahoo.com) + * Fix decoding issue to be more permissive, which corrects nesting errors + for broken [column] tags and other class-nesting issues. + * Add support for removing floating ending tags whose start tags had been + consumed during output-generation. + +2008-07-14 / v1.0 RC / Phantom Inker (inker2576@yahoo.com) + * Proposed final release candidate: Implements full BBCode language, plus + smileys and wiki-links, with no apparent failures or caveats. + * Added conformance test script with 49 tests. + +2008-07-01 / alpha / Phantom Inker (inker2576@yahoo.com) + * First usable build; no version number. Performs basic compiling and stack + transforms; currently very limited in what it can process. + diff --git a/app/Plugin/comment/Lib/nbbc/LICENSE b/app/Plugin/comment/Lib/nbbc/LICENSE new file mode 100644 index 00000000..63a497cc --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/LICENSE @@ -0,0 +1,28 @@ + + Copyright (C) 2008-10, the Phantom Inker. All rights reserved. + Portions copyright (c) 2004-2008 AddedBytes.com + + 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. + + THIS SOFTWARE IS PROVIDED BY THE PHANTOM INKER "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. + diff --git a/app/Plugin/comment/Lib/nbbc/nbbc.php b/app/Plugin/comment/Lib/nbbc/nbbc.php new file mode 100644 index 00000000..34ad214d --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/nbbc.php @@ -0,0 +1,2049 @@ + '\[', '<' => '<', '{' => '\{', '(' => '\(' ); +$regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); +$endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); +if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; +$e = $regex_endmarkers[$tagmarker]; +$b = $regex_beginmarkers[$tagmarker]; +$this->tagmarker = $tagmarker; +$this->end_tagmarker = $endmarkers[$tagmarker]; +$this->pat_main = "/( " +. "{$b}" +. "(?! -- | ' | !-- | {$b}{$b} )" +. "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" +. "{$e}" +. "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" +. "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" +. "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" +. "| -----+" +. "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" +. "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" +. "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" +. " )/Dx"; +$this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); +$this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; +$this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; +$this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; +$this->ptr = 0; +$this->unget = false; +$this->state = BBCODE_LEXSTATE_TEXT; +$this->verbatim = false; +$this->token = BBCODE_EOI; +$this->tag = false; +$this->text = ""; +} +function GuessTextLength() { +$length = 0; +$ptr = 0; +$state = BBCODE_LEXSTATE_TEXT; +while ($ptr < count($this->input)) { +$text = $this->input[$ptr++]; +if ($state == BBCODE_LEXSTATE_TEXT) { +$state = BBCODE_LEXSTATE_TAG; +$length += strlen($text); +} +else { +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$state = BBCODE_LEXSTATE_TEXT; +$length++; +break; +default: +$state = BBCODE_LEXSTATE_TEXT; +$length += strlen($text); +break; +case 40: +case 60: +case 91: +case 123: +$state = BBCODE_LEXSTATE_TEXT; +break; +} +} +} +return $length; +} +function NextToken() { +if ($this->unget) { +$this->unget = false; +return $this->token; +} +while (true) { +if ($this->ptr >= count($this->input)) { +$this->text = ""; +$this->tag = false; +return $this->token = BBCODE_EOI; +} +$this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", +$this->input[$this->ptr++]); +if ($this->verbatim) { +$this->tag = false; +if ($this->state == BBCODE_LEXSTATE_TEXT) { +$this->state = BBCODE_LEXSTATE_TAG; +$token_type = BBCODE_TEXT; +} +else { +$this->state = BBCODE_LEXSTATE_TEXT; +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$token_type = BBCODE_NL; +break; +default: +$token_type = BBCODE_WS; +break; +case 45: +case 40: +case 60: +case 91: +case 123: +$token_type = BBCODE_TEXT; +break; +} +} +if (strlen($this->text) > 0) +return $this->token = $token_type; +} +else if ($this->state == BBCODE_LEXSTATE_TEXT) { +$this->state = BBCODE_LEXSTATE_TAG; +$this->tag = false; +if (strlen($this->text) > 0) +return $this->token = BBCODE_TEXT; +} +else { +switch (ord(substr($this->text, 0, 1))) { +case 10: +case 13: +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_NL; +case 45: +if (preg_match("/^-----/", $this->text)) { +$this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_TAG; +} +else { +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +if (strlen($this->text) > 0) +return $this->token = BBCODE_TEXT; +continue; +} +default: +$this->tag = false; +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_WS; +case 40: +case 60: +case 91: +case 123: +if (preg_match($this->pat_comment, $this->text)) { +$this->state = BBCODE_LEXSTATE_TEXT; +continue; +} +if (preg_match($this->pat_comment2, $this->text)) { +$this->state = BBCODE_LEXSTATE_TEXT; +continue; +} +if (preg_match($this->pat_wiki, $this->text, $matches)) { +$this->tag = Array('_name' => 'wiki', '_endtag' => false, +'_default' => @$matches[1], 'title' => @$matches[2]); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = BBCODE_TAG; +} +$this->tag = $this->Internal_DecodeTag($this->text); +$this->state = BBCODE_LEXSTATE_TEXT; +return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); +} +} +} +} +function UngetToken() { +if ($this->token !== BBCODE_EOI) +$this->unget = true; +} +function PeekToken() { +$result = $this->NextToken(); +if ($this->token !== BBCODE_EOI) +$this->unget = true; +return $result; +} +function SaveState() { +return Array( +'token' => $this->token, +'text' => $this->text, +'tag' => $this->tag, +'state' => $this->state, +'input' => $this->input, +'ptr' => $this->ptr, +'unget' => $this->unget, +'verbatim' => $this->verbatim +); +} +function RestoreState($state) { +if (!is_array($state)) return; +$this->token = @$state['token']; +$this->text = @$state['text']; +$this->tag = @$state['tag']; +$this->state = @$state['state']; +$this->input = @$state['input']; +$this->ptr = @$state['ptr']; +$this->unget = @$state['unget']; +$this->verbatim = @$state['verbatim']; +} +function Internal_StripQuotes($string) { +if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) +return $matches[1]; +else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) +return $matches[1]; +else return $string; +} +function Internal_ClassifyPiece($ptr, $pieces) { +if ($ptr >= count($pieces)) return -1; +$piece = $pieces[$ptr]; +if ($piece == '=') return '='; +else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; +else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; +else return 'A'; +} +function Internal_DecodeTag($tag) { +$result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', +'_hasend' => false, '_end' => false, '_default' => false); +$tag = substr($tag, 1, strlen($tag)-2); +$ch = ord(substr($tag, 0, 1)); +if ($ch >= 0 && $ch <= 32) return $result; +$pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", +$tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); +$ptr = 0; +if (count($pieces) < 1) return $result; +if (@substr($pieces[$ptr], 0, 1) == '/') { +$result['_name'] = strtolower(substr($pieces[$ptr++], 1)); +$result['_end'] = true; +} +else { +$result['_name'] = strtolower($pieces[$ptr++]); +$result['_end'] = false; +} +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +$params = Array(); +if ($type != '=') { +$result['_default'] = false; +$params[] = Array('key' => '', 'value' => ''); +} +else { +$ptr++; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type == "\"") +$value = $this->Internal_StripQuotes($pieces[$ptr++]); +else { +$after_space = false; +$start = $ptr; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { +if ($type == ' ') $after_space = true; +if ($type == '=' && $after_space) break; +$ptr++; +} +if ($type == -1) $ptr--; +if ($type == '=') { +$ptr--; +while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') +$ptr--; +while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') +$ptr--; +} +$value = ""; +for (; $start <= $ptr; $start++) { +if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') +$value .= " "; +else $value .= $this->Internal_StripQuotes($pieces[$start]); +} +$value = trim($value); +$ptr++; +} +$result['_default'] = $value; +$params[] = Array('key' => '', 'value' => $value); +} +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { +while ($type == ' ') { +$ptr++; +$type = $this->Internal_ClassifyPiece($ptr, $pieces); +} +if ($type == 'A' || $type == '"') +$key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); +else if ($type == '=') { +$ptr++; +continue; +} +else if ($type == -1) break; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type != '=') +$value = $this->Internal_StripQuotes($key); +else { +$ptr++; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') +$ptr++; +if ($type == '"') { +$value = $this->Internal_StripQuotes($pieces[$ptr++]); +} +else if ($type != -1) { +$value = $pieces[$ptr++]; +while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 +&& $type != ' ') +$value .= $pieces[$ptr++]; +} +else $value = ""; +} +if (substr($key, 0, 1) != '_') +$result[$key] = $value; +$params[] = Array('key' => $key, 'value' => $value); +} +$result['_params'] = $params; +return $result; +} +} + +class BBCodeLibrary { +var $default_smileys = Array( +':)' => 'smile.gif', ':-)' => 'smile.gif', +'=)' => 'smile.gif', '=-)' => 'smile.gif', +':(' => 'frown.gif', ':-(' => 'frown.gif', +'=(' => 'frown.gif', '=-(' => 'frown.gif', +':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', +'=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', +'>:('=> 'angry.gif', '>:-('=> 'angry.gif', +'>=('=> 'angry.gif', '>=-('=> 'angry.gif', +'D:' => 'angry.gif', 'D-:' => 'angry.gif', +'D=' => 'angry.gif', 'D-=' => 'angry.gif', +'>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', +'>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', +'>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', +'>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', +'>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', +'>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', +'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', +'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', +':O' => 'surprise.gif', ':-O' => 'surprise.gif', +'=O' => 'surprise.gif', '=-O' => 'surprise.gif', +':?' => 'confuse.gif', ':-?' => 'confuse.gif', +'=?' => 'confuse.gif', '=-?' => 'confuse.gif', +':s' => 'worry.gif', ':-S' => 'worry.gif', +'=s' => 'worry.gif', '=-S' => 'worry.gif', +':|' => 'neutral.gif', ':-|' => 'neutral.gif', +'=|' => 'neutral.gif', '=-|' => 'neutral.gif', +':I' => 'neutral.gif', ':-I' => 'neutral.gif', +'=I' => 'neutral.gif', '=-I' => 'neutral.gif', +':/' => 'irritated.gif', ':-/' => 'irritated.gif', +'=/' => 'irritated.gif', '=-/' => 'irritated.gif', +':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', +'=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', +':P' => 'tongue.gif', ':-P' => 'tongue.gif', +'=P' => 'tongue.gif', '=-P' => 'tongue.gif', +'X-P' => 'tongue.gif', +'8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', +'B)' => 'cool.gif', 'B-)' => 'cool.gif', +';)' => 'wink.gif', ';-)' => 'wink.gif', +';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', +'^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', +'>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', +'<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', +'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', +';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', +':3' => 'smile3.gif', ':-3' => 'smile3.gif', +'=3' => 'smile3.gif', '=-3' => 'smile3.gif', +';3' => 'wink3.gif', ';-3' => 'wink3.gif', +'' => 'teeth.gif', '' => 'teeth.gif', +'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', +':blue:' => 'blue.gif', +':zzz:' => 'sleepy.gif', +'<3' => 'heart.gif', +':star:' => 'star.gif', +); +var $default_tag_rules = Array( +'b' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'i' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'u' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'s' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'plain_start' => "", +'plain_end' => "", +), +'font' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), +'method' => 'DoFont', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'color' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), +'template' => '{$_content/v}', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'size' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'allow' => Array('_default' => '/^[0-9.]+$/D'), +'method' => 'DoSize', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'sup' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'sub' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'spoiler' => Array( +'simple_start' => "", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'acronym' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'template' => '{$_content/v}', +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +), +'url' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoURL', +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'content' => BBCODE_REQUIRED, +'plain_start' => "", +'plain_end' => "", +'plain_content' => Array('_content', '_default'), +'plain_link' => Array('_default', '_content'), +), +'email' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoEmail', +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'content' => BBCODE_REQUIRED, +'plain_start' => "", +'plain_end' => "", +'plain_content' => Array('_content', '_default'), +'plain_link' => Array('_default', '_content'), +), +'wiki' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoWiki", +'class' => 'link', +'allow_in' => Array('listitem', 'block', 'columns', 'inline'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'plain_start' => "[", +'plain_end' => "]", +'plain_content' => Array('title', '_default'), +'plain_link' => Array('_default', '_content'), +), +'img' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoImage", +'class' => 'image', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'end_tag' => BBCODE_REQUIRED, +'content' => BBCODE_REQUIRED, +'plain_start' => "[image]", +'plain_content' => Array(), +), +'rule' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoRule", +'class' => 'block', +'allow_in' => Array('listitem', 'block', 'columns'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "sns", +'after_tag' => "sns", +'plain_start' => "\n-----\n", +'plain_end' => "", +'plain_content' => Array(), +), +'br' => Array( +'mode' => BBCODE_MODE_SIMPLE, +'simple_start' => "
    \n", +'simple_end' => "", +'class' => 'inline', +'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "s", +'after_tag' => "s", +'plain_start' => "\n", +'plain_end' => "", +'plain_content' => Array(), +), +'left' => Array( +'simple_start' => "\n
    \n", +'simple_end' => "\n
    \n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'right' => Array( +'simple_start' => "\n
    \n", +'simple_end' => "\n
    \n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'center' => Array( +'simple_start' => "\n
    \n", +'simple_end' => "\n
    \n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'indent' => Array( +'simple_start' => "\n
    \n", +'simple_end' => "\n
    \n", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'columns' => Array( +'simple_start' => "\n
    \n", +'simple_end' => "\n
    \n", +'class' => 'columns', +'allow_in' => Array('listitem', 'block', 'columns'), +'end_tag' => BBCODE_REQUIRED, +'content' => BBCODE_REQUIRED, +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'nextcol' => Array( +'simple_start' => "\n\n", +'class' => 'nextcol', +'allow_in' => Array('columns'), +'end_tag' => BBCODE_PROHIBIT, +'content' => BBCODE_PROHIBIT, +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "", +), +'code' => Array( +'mode' => BBCODE_MODE_ENHANCED, +'template' => "\n
    \n
    Code:
    \n
    {\$_content/v}
    \n
    \n", +'class' => 'code', +'allow_in' => Array('listitem', 'block', 'columns'), +'content' => BBCODE_VERBATIM, +'before_tag' => "sns", +'after_tag' => "sn", +'before_endtag' => "sn", +'after_endtag' => "sns", +'plain_start' => "\nCode:\n", +'plain_end' => "\n", +), +'quote' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => "DoQuote", +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\nQuote:\n", +'plain_end' => "\n", +), +'list' => Array( +'mode' => BBCODE_MODE_LIBRARY, +'method' => 'DoList', +'class' => 'list', +'allow_in' => Array('listitem', 'block', 'columns'), +'before_tag' => "sns", +'after_tag' => "sns", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n", +'plain_end' => "\n", +), +'*' => Array( +'simple_start' => "
  • ", +'simple_end' => "
  • \n", +'class' => 'listitem', +'allow_in' => Array('list'), +'end_tag' => BBCODE_OPTIONAL, +'before_tag' => "s", +'after_tag' => "s", +'before_endtag' => "sns", +'after_endtag' => "sns", +'plain_start' => "\n * ", +'plain_end' => "\n", +), +); +function DoURL($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); +if ($bbcode->IsValidURL($url)) { +if ($bbcode->debug) +print "ISVALIDURL
    "; +if ($bbcode->url_targetable !== false && isset($params['target'])) +$target = " target=\"" . htmlspecialchars($params['target']) . "\""; +else $target = ""; +if ($bbcode->url_target !== false) +if (!($bbcode->url_targetable == 'override' && isset($params['target']))) +$target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; +return '' . $content . ''; +} +else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); +} +function DoEmail($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); +if ($bbcode->IsValidEmail($email)) +return '' . $content . ''; +else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); +} +function DoSize($bbcode, $action, $name, $default, $params, $content) { +switch ($default) { +case '0': $size = '.5em'; break; +case '1': $size = '.67em'; break; +case '2': $size = '.83em'; break; +default: +case '3': $size = '1.0em'; break; +case '4': $size = '1.17em'; break; +case '5': $size = '1.5em'; break; +case '6': $size = '2.0em'; break; +case '7': $size = '2.5em'; break; +} +return "$content"; +} +function DoFont($bbcode, $action, $name, $default, $params, $content) { +$fonts = explode(",", $default); +$result = ""; +$special_fonts = Array( +'serif' => 'serif', +'sans-serif' => 'sans-serif', +'sans serif' => 'sans-serif', +'sansserif' => 'sans-serif', +'sans' => 'sans-serif', +'cursive' => 'cursive', +'fantasy' => 'fantasy', +'monospace' => 'monospace', +'mono' => 'monospace', +); +foreach ($fonts as $font) { +$font = trim($font); +if (isset($special_fonts[$font])) { +if (strlen($result) > 0) $result .= ","; +$result .= $special_fonts[$font]; +} +else if (strlen($font) > 0) { +if (strlen($result) > 0) $result .= ","; +$result .= "'$font'"; +} +} +return "$content"; +} +function DoWiki($bbcode, $action, $name, $default, $params, $content) { +$name = $bbcode->Wikify($default); +if ($action == BBCODE_CHECK) +return strlen($name) > 0; +$title = trim(@$params['title']); +if (strlen($title) <= 0) $title = trim($default); +return "wiki_url}$name\" class=\"bbcode_wiki\">" +. htmlspecialchars($title) . ""; +} +function DoImage($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +$content = trim($bbcode->UnHTMLEncode(strip_tags($content))); +if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { +if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { +if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { +$info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); +if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { +return "local_img_url}/{$content}") . "\" alt=\"" +. htmlspecialchars(basename($content)) . "\" width=\"" +. htmlspecialchars($info[0]) . "\" height=\"" +. htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; +} +} +} +else if ($bbcode->IsValidURL($content, false)) { +return "\"""; +} +} +return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); +} +function DoRule($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +else return $bbcode->rule_html; +} +function DoQuote($bbcode, $action, $name, $default, $params, $content) { +if ($action == BBCODE_CHECK) return true; +if (isset($params['name'])) { +$title = htmlspecialchars(trim($params['name'])) . " wrote"; +if (isset($params['date'])) +$title .= " on " . htmlspecialchars(trim($params['date'])); +$title .= ":"; +if (isset($params['url'])) { +$url = trim($params['url']); +if ($bbcode->IsValidURL($url)) +$title = "" . $title . ""; +} +} +else if (!is_string($default)) +$title = "Quote:"; +else $title = htmlspecialchars(trim($default)) . " wrote:"; +return "\n
    \n
    " +. $title . "
    \n
    " +. $content . "
    \n
    \n"; +} +function DoList($bbcode, $action, $name, $default, $params, $content) { +$list_styles = Array( +'1' => 'decimal', +'01' => 'decimal-leading-zero', +'i' => 'lower-roman', +'I' => 'upper-roman', +'a' => 'lower-alpha', +'A' => 'upper-alpha', +); +$ci_list_styles = Array( +'circle' => 'circle', +'disc' => 'disc', +'square' => 'square', +'greek' => 'lower-greek', +'armenian' => 'armenian', +'georgian' => 'georgian', +); +$ul_types = Array( +'circle' => 'circle', +'disc' => 'disc', +'square' => 'square', +); +$default = trim($default); +if ($action == BBCODE_CHECK) { +if (!is_string($default) || strlen($default) == "") return true; +else if (isset($list_styles[$default])) return true; +else if (isset($ci_list_styles[strtolower($default)])) return true; +else return false; +} +if (!is_string($default) || strlen($default) == "") { +$elem = 'ul'; +$type = ''; +} +else if ($default == '1') { +$elem = 'ol'; +$type = ''; +} +else if (isset($list_styles[$default])) { +$elem = 'ol'; +$type = $list_styles[$default]; +} +else { +$default = strtolower($default); +if (isset($ul_types[$default])) { +$elem = 'ul'; +$type = $ul_types[$default]; +} +else if (isset($ci_list_styles[$default])) { +$elem = 'ol'; +$type = $ci_list_styles[$default]; +} +} +if (strlen($type)) +return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content\n"; +else return "\n<$elem class=\"bbcode_list\">\n$content\n"; +} +} + +class BBCodeEmailAddressValidator { +function check_email_address($strEmailAddress) { +if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { +return false; +} +$intAtSymbol = strrpos($strEmailAddress, '@'); +if ($intAtSymbol === false) { +return false; +} +$arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); +$arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); +$arrTempAddress[0] = preg_replace('/"[^"]+"/' +,'' +,$arrEmailAddress[0]); +$arrTempAddress[1] = $arrEmailAddress[1]; +$strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; +if (strrpos($strTempAddress, '@') !== false) { +return false; +} +if (!$this->check_local_portion($arrEmailAddress[0])) { +return false; +} +if (!$this->check_domain_portion($arrEmailAddress[1])) { +return false; +} +return true; +} +function check_local_portion($strLocalPortion) { +if (!$this->check_text_length($strLocalPortion, 1, 64)) { +return false; +} +$arrLocalPortion = explode('.', $strLocalPortion); +for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { +if (!preg_match('.^(' +. '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' +. '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' +.'|' +. '("[^\\\"]{0,62}")' +.')$.' +,$arrLocalPortion[$i])) { +return false; +} +} +return true; +} +function check_domain_portion($strDomainPortion) { +if (!$this->check_text_length($strDomainPortion, 1, 255)) { +return false; +} +if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' +.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' +,$strDomainPortion) || +preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' +.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' +,$strDomainPortion)) { +return true; +} else { +$arrDomainPortion = explode('.', $strDomainPortion); +if (sizeof($arrDomainPortion) < 2) { +return false; +} +for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { +if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { +return false; +} +if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' +.'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { +return false; +} +} +} +return true; +} +function check_text_length($strText, $intMinimum, $intMaximum) { +$intTextLength = strlen($strText); +if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { +return false; +} else { +return true; +} +} +} + +class BBCode { +var $tag_rules; +var $defaults; +var $current_class; +var $root_class; +var $lost_start_tags; +var $start_tags; +var $allow_ampersand; +var $tag_marker; +var $ignore_newlines; +var $plain_mode; +var $detect_urls; +var $url_pattern; +var $output_limit; +var $text_length; +var $was_limited; +var $limit_tail; +var $limit_precision; +var $smiley_dir; +var $smiley_url; +var $smileys; +var $smiley_regex; +var $enable_smileys; +var $wiki_url; +var $local_img_dir; +var $local_img_url; +var $url_targetable; +var $url_target; +var $rule_html; +var $pre_trim; +var $post_trim; +var $debug; +function BBCode() { +$this->defaults = new BBCodeLibrary; +$this->tag_rules = $this->defaults->default_tag_rules; +$this->smileys = $this->defaults->default_smileys; +$this->enable_smileys = true; +$this->smiley_regex = false; +$this->smiley_dir = $this->GetDefaultSmileyDir(); +$this->smiley_url = $this->GetDefaultSmileyURL(); +$this->wiki_url = $this->GetDefaultWikiURL(); +$this->local_img_dir = $this->GetDefaultLocalImgDir(); +$this->local_img_url = $this->GetDefaultLocalImgURL(); +$this->rule_html = $this->GetDefaultRuleHTML(); +$this->pre_trim = ""; +$this->post_trim = ""; +$this->root_class = 'block'; +$this->lost_start_tags = Array(); +$this->start_tags = Array(); +$this->tag_marker = '['; +$this->allow_ampsersand = false; +$this->current_class = $this->root_class; +$this->debug = false; +$this->ignore_newlines = false; +$this->output_limit = 0; +$this->plain_mode = false; +$this->was_limited = false; +$this->limit_tail = "..."; +$this->limit_precision = 0.15; +$this->detect_urls = false; +$this->url_pattern = '{$text/h}'; +$this->url_targetable = false; +$this->url_target = false; +} +function SetPreTrim($trim = "a") { $this->pre_trim = $trim; } +function GetPreTrim() { return $this->pre_trim; } +function SetPostTrim($trim = "a") { $this->post_trim = $trim; } +function GetPostTrim() { return $this->post_trim; } +function SetRoot($class = 'block') { $this->root_class = $class; } +function SetRootInline() { $this->root_class = 'inline'; } +function SetRootBlock() { $this->root_class = 'block'; } +function GetRoot() { return $this->root_class; } +function SetDebug($enable = true) { $this->debug = $enable; } +function GetDebug() { return $this->debug; } +function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; } +function GetAllowAmpersand() { return $this->allow_ampersand; } +function SetTagMarker($marker = '[') { $this->tag_marker = $marker; } +function GetTagMarker() { return $this->tag_marker; } +function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; } +function GetIgnoreNewlines() { return $this->ignore_newlines; } +function SetLimit($limit = 0) { $this->output_limit = $limit; } +function GetLimit() { return $this->output_limit; } +function SetLimitTail($tail = "...") { $this->limit_tail = $tail; } +function GetLimitTail() { return $this->limit_tail; } +function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; } +function GetLimitPrecision() { return $this->limit_precision; } +function WasLimited() { return $this->was_limited; } +function SetPlainMode($enable = true) { $this->plain_mode = $enable; } +function GetPlainMode() { return $this->plain_mode; } +function SetDetectURLs($enable = true) { $this->detect_urls = $enable; } +function GetDetectURLs() { return $this->detect_urls; } +function SetURLPattern($pattern) { $this->url_pattern = $pattern; } +function GetURLPattern() { return $this->url_pattern; } +function SetURLTargetable($enable) { $this->url_targetable = $enable; } +function GetURLTargetable() { return $this->url_targetable; } +function SetURLTarget($target) { $this->url_target = $target; } +function GetURLTarget() { return $this->url_target; } +function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; } +function RemoveRule($name) { unset($this->tag_rules[$name]); } +function GetRule($name) { return isset($this->tag_rules[$name]) +? $this->tag_rules[$name] : false; } +function ClearRules() { $this->tag_rules = Array(); } +function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name]) +? $this->defaults->default_tag_rules[$name] : false; } +function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name])) +$this->AddRule($name, $this->defaults->default_tag_rules[$name]); +else $this->RemoveRule($name); } +function GetDefaultRules() { return $this->defaults->default_tag_rules; } +function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; } +function SetWikiURL($url) { $this->wiki_url = $url; } +function GetWikiURL($url) { return $this->wiki_url; } +function GetDefaultWikiURL() { return '/?page='; } +function SetLocalImgDir($path) { $this->local_img_dir = $path; } +function GetLocalImgDir() { return $this->local_img_dir; } +function GetDefaultLocalImgDir() { return "img"; } +function SetLocalImgURL($path) { $this->local_img_url = $path; } +function GetLocalImgURL() { return $this->local_img_url; } +function GetDefaultLocalImgURL() { return "img"; } +function SetRuleHTML($html) { $this->rule_html = $html; } +function GetRuleHTML() { return $this->rule_html; } +function GetDefaultRuleHTML() { return "\n
    \n"; } +function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; } +function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; } +function GetSmiley($code) { return isset($this->smileys[$code]) +? $this->smileys[$code] : false; } +function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; } +function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code]) +? $this->defaults->default_smileys[$code] : false; } +function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code]; +$this->smiley_regex = false; } +function GetDefaultSmileys() { return $this->defaults->default_smileys; } +function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys; +$this->smiley_regex = false; } +function SetSmileyDir($path) { $this->smiley_dir = $path; } +function GetSmileyDir() { return $this->smiley_dir; } +function GetDefaultSmileyDir() { return "smileys"; } +function SetSmileyURL($path) { $this->smiley_url = $path; } +function GetSmileyURL() { return $this->smiley_url; } +function GetDefaultSmileyURL() { return "smileys"; } +function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; } +function GetEnableSmileys() { return $this->enable_smileys; } +function nl2br($string) { +return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "
    \n", $string); +} +function UnHTMLEncode($string) { +if (function_exists("html_entity_decode")) +return html_entity_decode($string); +$string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); +$string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string); +$trans_tbl = get_html_translation_table(HTML_ENTITIES); +$trans_tbl = array_flip($trans_tbl); +return strtr($string, $trans_tbl); +} +function Wikify($string) { +return rawurlencode(str_replace(" ", "_", +trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string)))); +} +function IsValidURL($string, $email_too = true) { +if (preg_match("/^ +(?:https?|ftp):\\/\\/ +(?: +(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+ +[a-zA-Z0-9] +(?:[a-zA-Z0-9-]*[a-zA-Z0-9])? +| +\\[ +(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} +(?: +25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]: +(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ +) +\\] +) +(?::[0-9]{1,5})? +(?:[\\/\\?\\#][^\\n\\r]*)? +$/Dx", $string)) return true; +if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) +return true; +if ($email_too) +if (substr($string, 0, 7) == "mailto:") +return $this->IsValidEmail(substr($string, 7)); +return false; +} +function IsValidEmail($string) { +$validator = new BBCodeEmailAddressValidator; +return $validator->check_email_address($string); +/* +return preg_match("/^ +(?: +[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+ +(?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)* +| +\"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\" +) +@ +(?: +(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ +[a-z0-9] +(?:[a-z0-9-]*[a-z0-9])? +| +\\[ +(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} +(?: +25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: +(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] +|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ +) +\\] +) +$/Dx", $string); +*/ +} +function HTMLEncode($string) { +if (!$this->allow_ampersand) +return htmlspecialchars($string); +else return str_replace(Array('<', '>', '"'), +Array('<', '>', '"'), $string); +} +function FixupOutput($string) { +if (!$this->detect_urls) { +$output = $this->Internal_ProcessSmileys($string); +} +else { +$chunks = $this->Internal_AutoDetectURLs($string); +$output = Array(); +if (count($chunks)) { +$is_a_url = false; +foreach ($chunks as $index => $chunk) { +if (!$is_a_url) { +$chunk = $this->Internal_ProcessSmileys($chunk); +} +$output[] = $chunk; +$is_a_url = !$is_a_url; +} +} +$output = implode("", $output); +} +return $output; +} +function Internal_ProcessSmileys($string) { +if (!$this->enable_smileys || $this->plain_mode) { +$output = $this->HTMLEncode($string); +} +else { +if ($this->smiley_regex === false) { +$this->Internal_RebuildSmileys(); +} +$tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE); +if (count($tokens) <= 1) { +$output = $this->HTMLEncode($string); +} +else { +$output = ""; +$is_a_smiley = false; +foreach ($tokens as $token) { +if (!$is_a_smiley) { +$output .= $this->HTMLEncode($token); +} +else { +if (isset($this->smiley_info[$token])) { +$info = $this->smiley_info[$token]; +} +else { +$info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]); +$this->smiley_info[$token] = $info; +} +$alt = htmlspecialchars($token); +$output .= "smiley_url . '/' . $this->smileys[$token]) +. "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" +. " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />"; +} +$is_a_smiley = !$is_a_smiley; +} +} +} +return $output; +} +function Internal_RebuildSmileys() { +$regex = Array("/(?smileys as $code => $filename) { +if (!$first) $regex[] = "|"; +$regex[] = preg_quote("$code", '/'); +$first = false; +} +$regex[] = ")(?![\\w])/"; +$this->smiley_regex = implode("", $regex); +} +function Internal_AutoDetectURLs($string) { +$output = preg_split("/( (?: +(?:https?|ftp) : \\/* +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} ) +) +(?: : [0-9]+ )? +(?! [a-zA-Z0-9.:-] ) +(?: +\\/ +[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* +)? +(?: +[?#] +[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ +)? +) | (?: +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +) +(?: : [0-9]+ )? +(?! [a-zA-Z0-9.:-] ) +(?: +\\/ +[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* +)? +(?: +[?#] +[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ +)? +) | (?: +[a-zA-Z0-9._-]{2,} @ +(?: +(?: (?: [a-zA-Z0-9-]{2,} \\. )+ +(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} +| aero | biz | coop | info | museum | name | pro +| example | invalid | localhost | test | local | onion | swift)) +| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) +) +))/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE); +if (count($output) > 1) { +$is_a_url = false; +foreach ($output as $index => $token) { +if ($is_a_url) { +if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) { +$url = "mailto:" . $token; +} +else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) { +$url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3]; +} +else { +preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches); +$url = "http:/" . "/" . $matches[1] . "/" . $matches[2]; +} +$params = @parse_url($url); +if (!is_array($params)) $params = Array(); +$params['url'] = $url; +$params['link'] = $url; +$params['text'] = $token; +$output[$index] = $this->FillTemplate($this->url_pattern, $params); +} +$is_a_url = !$is_a_url; +} +} +return $output; +} +function FillTemplate($template, $insert_array, $default_array = Array()) { +$pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template, +-1, PREG_SPLIT_DELIM_CAPTURE); +if (count($pieces) <= 1) +return $template; +$result = Array(); +$is_an_insert = false; +foreach ($pieces as $piece) { +if (!$is_an_insert) { +$result[] = $piece; +} +else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) { +$result[] = $piece; +} +else { +if (isset($insert_array[$matches[1]])) +$value = @$insert_array[$matches[1]]; +else $value = @$default_array[$matches[1]]; +if (strlen(@$matches[2])) { +foreach (split(".", substr($matches[2], 1)) as $index) { +if (is_array($value)) +$value = @$value[$index]; +else if (is_object($value)) { +$value = (array)$value; +$value = @$value[$index]; +} +else $value = ""; +} +} +switch (gettype($value)) { +case 'boolean': $value = $value ? "true" : "false"; break; +case 'integer': $value = (string)$value; break; +case 'double': $value = (string)$value; break; +case 'string': break; +default: $value = ""; break; +} +if (strlen(@$matches[3])) +$flags = array_flip(str_split($matches[3])); +else $flags = Array(); +if (!isset($flags['v'])) { +if (isset($flags['w'])) +$value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value); +if (isset($flags['t'])) $value = trim($value); +if (isset($flags['b'])) $value = basename($value); +if (isset($flags['e'])) $value = $this->HTMLEncode($value); +else if (isset($flags['k'])) $value = $this->Wikify($value); +else if (isset($flags['h'])) $value = htmlspecialchars($value); +else if (isset($flags['u'])) $value = urlencode($value); +if (isset($flags['n'])) $value = $this->nl2br($value); +} +$result[] = $value; +} +$is_an_insert = !$is_an_insert; +} +return implode("", $result); +} +function Internal_CollectText($array, $start = 0) { +ob_start(); +for ($start = intval($start), $end = count($array); $start < $end; $start++) +print $array[$start][BBCODE_STACK_TEXT]; +$output = ob_get_contents(); +ob_end_clean(); +return $output; +} +function Internal_CollectTextReverse($array, $start = 0, $end = 0) { +ob_start(); +for ($start = intval($start); $start >= $end; $start--) +print $array[$start][BBCODE_STACK_TEXT]; +$output = ob_get_contents(); +ob_end_clean(); +return $output; +} +function Internal_GenerateOutput($pos) { +$output = Array(); +while (count($this->stack) > $pos) { +$token = array_pop($this->stack); +if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) { +$output[] = $token; +} +else { +$name = @$token[BBCODE_STACK_TAG]['_name']; +$rule = @$this->tag_rules[$name]; +$end_tag = @$rule['end_tag']; +if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED; +else $end_tag = $rule['end_tag']; +array_pop($this->start_tags[$name]); +if ($end_tag == BBCODE_PROHIBIT) { +$output[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TAG => false, +BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT], +BBCODE_STACK_CLASS => $this->current_class, +); +} +else { +if ($end_tag == BBCODE_REQUIRED) +@$this->lost_start_tags[$name] += 1; +$end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output); +$this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output); +$tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end); +$this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack); +$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]); +$tag_output = $this->DoTag(BBCODE_OUTPUT, $name, +@$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body); +$output = Array(Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TAG => false, +BBCODE_STACK_TEXT => $tag_output, +BBCODE_STACK_CLASS => $this->current_class +)); +} +} +} +$this->Internal_ComputeCurrentClass(); +return $output; +} +function Internal_RewindToClass($class_list) { +$pos = count($this->stack) - 1; +while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) +$pos--; +if ($pos < 0) { +if (!in_array($this->root_class, $class_list)) +return false; +} +$output = $this->Internal_GenerateOutput($pos+1); +while (count($output)) { +$token = array_pop($output); +$token[BBCODE_STACK_CLASS] = $this->current_class; +$this->stack[] = $token; +} +return true; +} +function Internal_FinishTag($tag_name) { +if (strlen($tag_name) <= 0) +return false; +if (isset($this->start_tags[$tag_name]) +&& count($this->start_tags[$tag_name])) +$pos = array_pop($this->start_tags[$tag_name]); +else $pos = -1; +if ($pos < 0) return false; +$newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'], +$pos+1, $this->stack); +$delta = $newpos - ($pos+1); +$output = $this->Internal_GenerateOutput($newpos); +$newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'], +0, $output); +$output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend); +while ($delta-- > 0) +array_pop($this->stack); +$this->Internal_ComputeCurrentClass(); +return $output; +} +function Internal_ComputeCurrentClass() { +if (count($this->stack) > 0) +$this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS]; +else $this->current_class = $this->root_class; +} +function Internal_DumpStack($array = false, $raw = false) { +if (!$raw) $string = ""; +else $string = ""; +if ($array === false) +$array = $this->stack; +foreach ($array as $item) { +switch (@$item[BBCODE_STACK_TOKEN]) { +case BBCODE_TEXT: +$string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" "; +break; +case BBCODE_WS: +$string .= "WS "; +break; +case BBCODE_NL: +$string .= "NL "; +break; +case BBCODE_TAG: +$string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] "; +break; +default: +$string .= "unknown "; +break; +} +} +if (!$raw) $string .= ""; +return $string; +} +function Internal_CleanupWSByPoppingStack($pattern, &$array) { +if (strlen($pattern) <= 0) return; +$oldlen = count($array); +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS) +array_pop($array); +break; +case 'n': +if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL) +array_pop($array); +break; +case 'a': +while (count($array) > 0 +&& (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS +|| $token == BBCODE_NL)) +array_pop($array); +break; +} +} +if (count($array) != $oldlen) { +$this->Internal_ComputeCurrentClass(); +} +} +function Internal_CleanupWSByEatingInput($pattern) { +if (strlen($pattern) <= 0) return; +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +$token_type = $this->lexer->NextToken(); +while ($token_type == BBCODE_WS) { +$token_type = $this->lexer->NextToken(); +} +$this->lexer->UngetToken(); +break; +case 'n': +$token_type = $this->lexer->NextToken(); +if ($token_type != BBCODE_NL) +$this->lexer->UngetToken(); +break; +case 'a': +$token_type = $this->lexer->NextToken(); +while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) { +$token_type = $this->lexer->NextToken(); +} +$this->lexer->UngetToken(); +break; +} +} +} +function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) { +if (strlen($pattern) <= 0) return $pos; +foreach (str_split($pattern) as $char) { +switch ($char) { +case 's': +while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) +$pos++; +break; +case 'n': +if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) +$pos++; +break; +case 'a': +while ($pos < count($array) +&& (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) +$pos++; +break; +} +} +return $pos; +} +function Internal_LimitText($string, $limit) { +$chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE); +$output = ""; +foreach ($chunks as $chunk) { +if (strlen($output) + strlen($chunk) > $limit) +break; +$output .= $chunk; +} +$output = rtrim($output); +return $output; +} +function Internal_DoLimit() { +$this->Internal_CleanupWSByPoppingStack("a", $this->stack); +if (strlen($this->limit_tail) > 0) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->limit_tail, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->was_limited = true; +} +function DoTag($action, $tag_name, $default_value, $params, $contents) { +$tag_rule = @$this->tag_rules[$tag_name]; +switch ($action) { +case BBCODE_CHECK: +if (isset($tag_rule['allow'])) { +foreach ($tag_rule['allow'] as $param => $pattern) { +if ($param == '_content') $value = $contents; +else if ($param == '_defaultcontent') { +if (strlen($default_value)) +$value = $default_value; +else $value = $contents; +} +else { +if (isset($params[$param])) +$value = $params[$param]; +else $value = @$tag_rule['default'][$param]; +} +if (!preg_match($pattern, $value)) { +return false; +} +} +return true; +} +switch (@$tag_rule['mode']) { +default: +case BBCODE_MODE_SIMPLE: +$result = true; +break; +case BBCODE_MODE_ENHANCED: +$result = true; +break; +case BBCODE_MODE_INTERNAL: +$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_LIBRARY: +$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_CALLBACK: +$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK, +$tag_name, $default_value, $params, $contents); +break; +} +return $result; +case BBCODE_OUTPUT: +if ($this->plain_mode) { +if (!isset($tag_rule['plain_content'])) +$plain_content = Array('_content'); +else $plain_content = $tag_rule['plain_content']; +$result = $possible_content = ""; +foreach ($plain_content as $possible_content) { +if ($possible_content == '_content' +&& strlen($contents) > 0) { +$result = $contents; +break; +} +if (isset($params[$possible_content]) +&& strlen($params[$possible_content]) > 0) { +$result = htmlspecialchars($params[$possible_content]); +break; +} +} +$start = @$tag_rule['plain_start']; +$end = @$tag_rule['plain_end']; +if (isset($tag_rule['plain_link'])) { +$link = $possible_content = ""; +foreach ($tag_rule['plain_link'] as $possible_content) { +if ($possible_content == '_content' +&& strlen($contents) > 0) { +$link = $this->UnHTMLEncode(strip_tags($contents)); +break; +} +if (isset($params[$possible_content]) +&& strlen($params[$possible_content]) > 0) { +$link = $params[$possible_content]; +break; +} +} +$params = @parse_url($link); +if (!is_array($params)) $params = Array(); +$params['link'] = $link; +$params['url'] = $link; +$start = $this->FillTemplate($start, $params); +$end = $this->FillTemplate($end, $params); +} +return $start . $result . $end; +} +switch (@$tag_rule['mode']) { +default: +case BBCODE_MODE_SIMPLE: +$result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end']; +break; +case BBCODE_MODE_ENHANCED: +$result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents); +break; +case BBCODE_MODE_INTERNAL: +$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_LIBRARY: +$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +case BBCODE_MODE_CALLBACK: +$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT, +$tag_name, $default_value, $params, $contents); +break; +} +return $result; +default: +return false; +} +} +function Internal_DoEnhancedTag($tag_rule, $params, $contents) { +$params['_content'] = $contents; +$params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents; +return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']); +} +function Internal_UpdateParamsForMissingEndTag(&$params) { +switch ($this->tag_marker) { +case '[': $tail_marker = ']'; break; +case '<': $tail_marker = '>'; break; +case '{': $tail_marker = '}'; break; +case '(': $tail_marker = ')'; break; +default: $tail_marker = $this->tag_marker; break; +} +$params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker; +} +function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) { +if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, ""); +$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) { +$state = $this->lexer->SaveState(); +$end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker; +$start = count($this->stack); +$this->lexer->verbatim = true; +while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) { +if ($this->lexer->text == $end_tag) { +$end_tag_params = $this->lexer->tag; +break; +} +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$text = $this->Internal_LimitText($this->lexer->text, +$this->output_limit - $this->text_length); +if (strlen($text) > 0) { +$this->text_length += strlen($text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->Internal_DoLimit(); +break; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => $token_type, +BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text), +BBCODE_STACK_TAG => $this->lexer->tag, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->lexer->verbatim = false; +if ($token_type == BBCODE_EOI) { +$this->lexer->RestoreState($state); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack); +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack); +$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']); +$content = $this->Internal_CollectText($this->stack, $newstart); +array_splice($this->stack, $start); +$this->Internal_ComputeCurrentClass(); +$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); +$tag_params['_endtag'] = $end_tag_params['_tag']; +$tag_params['_hasend'] = true; +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, +@$tag_params['_default'], $tag_params, $content); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Internal_ParseStartTagToken() { +$tag_params = $this->lexer->tag; +$tag_name = @$tag_params['_name']; +if (!isset($this->tag_rules[$tag_name])) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +$tag_rule = $this->tag_rules[$tag_name]; +$allow_in = is_array($tag_rule['allow_in']) +? $tag_rule['allow_in'] : Array($this->root_class); +if (!in_array($this->current_class, $allow_in)) { +if (!$this->Internal_RewindToClass($allow_in)) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +} +$end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED; +if ($end_tag == BBCODE_PROHIBIT) { +$this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule); +return; +} +if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +return; +} +if (@$tag_rule['content'] == BBCODE_VERBATIM) { +$this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule); +return; +} +if (isset($tag_rule['class'])) +$newclass = $tag_rule['class']; +else $newclass = $this->root_class; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => $this->lexer->token, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => $this->lexer->tag, +BBCODE_STACK_CLASS => ($this->current_class = $newclass), +); +if (!isset($this->start_tags[$tag_name])) +$this->start_tags[$tag_name] = Array(count($this->stack)-1); +else $this->start_tags[$tag_name][] = count($this->stack)-1; +} +function Internal_ParseEndTagToken() { +$tag_params = $this->lexer->tag; +$tag_name = @$tag_params['_name']; +$contents = $this->Internal_FinishTag($tag_name); +if ($contents === false) { +if (@$this->lost_start_tags[$tag_name] > 0) { +$this->lost_start_tags[$tag_name]--; +} +else { +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +return; +} +$start_tag_node = array_pop($this->stack); +$start_tag_params = $start_tag_node[BBCODE_STACK_TAG]; +$this->Internal_ComputeCurrentClass(); +$this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack); +$start_tag_params['_endtag'] = $tag_params['_tag']; +$start_tag_params['_hasend'] = true; +$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'], +$start_tag_params, $contents); +$this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $output, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +function Parse($string) { +$this->lexer = new BBCodeLexer($string, $this->tag_marker); +$this->lexer->debug = $this->debug; +$old_output_limit = $this->output_limit; +if ($this->output_limit > 0) { +if (strlen($string) < $this->output_limit) { +$this->output_limit = 0; +} +else if ($this->limit_precision > 0) { +$guess_length = $this->lexer->GuessTextLength(); +if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) { +$this->output_limit = 0; +} +else { +} +} +} +$this->stack = Array(); +$this->start_tags = Array(); +$this->lost_start_tags = Array(); +$this->text_length = 0; +$this->was_limited = false; +if (strlen($this->pre_trim) > 0) +$this->Internal_CleanupWSByEatingInput($this->pre_trim); +$newline = $this->plain_mode ? "\n" : "
    \n"; +while (true) { +if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) { +break; +} +switch ($token_type) { +case BBCODE_TEXT: +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$text = $this->Internal_LimitText($this->lexer->text, +$this->output_limit - $this->text_length); +if (strlen($text) > 0) { +$this->text_length += strlen($text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_TEXT, +BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +break; +case BBCODE_WS: +if ($this->output_limit > 0 +&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += strlen($this->lexer->text); +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_WS, +BBCODE_STACK_TEXT => $this->lexer->text, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +break; +case BBCODE_NL: +if ($this->ignore_newlines) { +if ($this->output_limit > 0 +&& $this->text_length + 1 >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += 1; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_WS, +BBCODE_STACK_TEXT => "\n", +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +} +else { +$this->Internal_CleanupWSByPoppingStack("s", $this->stack); +if ($this->output_limit > 0 +&& $this->text_length + 1 >= $this->output_limit) { +$this->Internal_DoLimit(); +break 2; +} +$this->text_length += 1; +$this->stack[] = Array( +BBCODE_STACK_TOKEN => BBCODE_NL, +BBCODE_STACK_TEXT => $newline, +BBCODE_STACK_TAG => false, +BBCODE_STACK_CLASS => $this->current_class, +); +$this->Internal_CleanupWSByEatingInput("s"); +} +break; +case BBCODE_TAG: +$this->Internal_ParseStartTagToken(); +break; +case BBCODE_ENDTAG: +$this->Internal_ParseEndTagToken(); +break; +default: +break; +} +} +if (strlen($this->post_trim) > 0) +$this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack); +$result = $this->Internal_GenerateOutput(0); +$result = $this->Internal_CollectTextReverse($result, count($result) - 1); +$this->output_limit = $old_output_limit; +if ($this->plain_mode) { +$result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result); +$result = preg_replace("/(?:[\\x20]*\\n) {2,}[\\x20]*/", "\n\n", $result); +$result = trim($result); +} +return $result; +} +} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/powered_by_nbbc.png b/app/Plugin/comment/Lib/nbbc/powered_by_nbbc.png new file mode 100644 index 0000000000000000000000000000000000000000..0e45f18675fdd5c26ac56a4c3a1d4b555fe5c023 GIT binary patch literal 1821 zcmV+&2jcjNP)l^rLI zCnl37B%35Hn=LGsEHsKVJB2$ln=>|;HYTAaG@~>#sxv&TJUpd5II}oSflW?~PDX@A zNtQ`dgHu$CR9=Q&TZ3C$jayrgTV0Y}SCv;xq)bJ%MMbOHnZkcXvkZp65b8?z;dzO28ntFYmeS4jIYo=>zvub^$eRG_1c)WN_!%R}f zQd7)RTg_Wn(pOv5TVL5%X@0%YJ#AGjH8T=tc`=JgO0O~ zjJ}M7wS=6loRg`Ok+hMOww05!la{-doVJ{qyqTN4o2Ij-q_m`?yQ8MQrmDTFvAeOe zy|aqfijv2Ykin3X$di=Gl$ykvoy?t&%#WDmn4`g?rOu_H%b}{ms;=2-P*zC!OZE*&GOCL;@jTg-reNg+3wlc z_t@g);^yY&=jZ3;=H=kx;O6P(;OXG#=;-O`>FVn0>+9?3>gnw4?CtIC?(XjI@9*mF z>g@9D?ECEI`R4HO@bU5S^Yioa^78cb^!E1l_xJbm|MK|w`1<<#`}_O+{QUm@{{R2~ zWx8C$00001bW%=J06^y0W&i*H32;bRa{vGi!vFvd!vV){sAK>D010$OSad^gZEa<4 zbN~Q}DvbsJ00d!4L_t(|oUM2#pLU+AMtIOsk_52paa-=ok4i!Su(bo4S5A{Xe@znI3xe~V&}u7fC|I!JdE z=n`(h+Y!dG@n8Q3gM-l^Mg7ZS8oO==gRw_2s8L0W4m+ve!q>^d~$-Xg4rnOJmOaSs{}_UIIrcy zN*MtT>rOsY@Vbs>xjy)$qiyQ}-drnK*Q@>h{kh#SBr;%QK8R-Pg&X#owT+D;D@+wb zOP~tZHY$9UJ8ve`aJ8`}^cmaUv@L?6y>+9=2D+2;bGy&yezB!NqOqXx>G~`ZD(dSO zOe_~j+&dO1oHP~rd>qrH0t!u&wLag?g`a@keskgO40>u~H3Q-7`qPk*g7mCCbHlDE zO{^aoVC_*9x6}e=;lMx-;R_nQ+2kQN*8=96#-{82{L<1Y5)7QC#xgDYyIV?-3z`~vhY{SP^F1hp zgxynmT9`FM=8NwY^AEY{NGKAgr52}s)ejHZ5^-vJNh)Izmz((8pnGyx$2>6+3z zVJ~2|qq#k`>~e6TO)j}owMh`m8CQ%1;nYuc zp@$)1=fqpDTu4cYVSJICl)njYXurIC zIW9jTCM5s3<*6D)srsAwnv+I8#wAqudpz5Yt|uzA1j0Q?%TQ~J%Di_P``f+$WXH7)n_1P;I@E$T8mw;{ z^5|iwN@d?(IWd0004iP)t-s{{a6j zd@VG4G%s~8NPb8fh#DJ`8!Cz_K8!v)i90oiH93onKy_URax0Nu)_ftVcw&L`}I(Sg2THmtu37b9S6|dYyV;hhSl#VPm6X zWTj+fp=D;MW@xKuYOZQ;o^NZhYi+b`X0&E~qJ4X#dvK?4ak+7HzjS!8ct^xXQq59R z$5U3?R$Ad&c*b~q%zSdna(mNzV%B19^=)J5V}PcBfun(itA&iLjE|*{hq#B8t(BCr zl$^Ajp1Gc(vZ1B5rK7r|sky1Fy{v@6goN9Kn8=u%*_@Z=m#M<3rPZaQ)1#`}s;Akf zv%#~w#=E%PxU|%?u%+;<>uw$=_>>gww4?d|UF?(6I8?)2{O_V4fa@9*&M$Wm_FwOSg?f6*2Y!>eGxwc2_k-y6jtH+X8u;pUO zs2({zjmGYD{V@v5DWf;xqM>k-)7iTxFn+?k-Aveqs<-r2IWZ#m#FY+Zhmy9 zQg%tIx^_(#b54?Iw|m%PDvFY8H!HosQXpQUf2EDQ@(&JC6t7uP6h=sKc5iytE{fHu zU@S5{MT^DYG`SgcaiZ%zDZap7gFxVN8(GdQ%TOI`7-NdX7}-h8Paf@zVO)0a(0Jk~ zjM*JGk6oWgFspEe;~Zuwm=L(wMuy7Ba>p;)1VNw}kdcHSlpT9xg79du(t8QQhV&4`KzBU7L;ME);8PJTnVeYw0000c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddvl*m0L^u{KIM~P`Eh18|;2|R;t1y#5!-7Re zy9C(%Y#0<6npijmSQtJCFgP;vx@!eo*r?>*$`qg!u`}V|sR^o#A`>DD7@M0cct!41 zGB}=Rbr6&hSQVVc#A=$$CfoJNp=qM5s6~oa!wSb{Ufz@|kqob|Gq7>ZXjy4?fPtBf jtw(3V#03mYte;*j-5vJWg{gtl;f?l3?T2iPj11NQMmA6W literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/anime.gif b/app/Plugin/comment/Lib/nbbc/smileys/anime.gif new file mode 100644 index 0000000000000000000000000000000000000000..24df9493ddb4e7538039a97b252f2633e0a66e95 GIT binary patch literal 240 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodda~Y&LL^u{KIM~P`Eh18|;2|R;t1weS$AU#i z+ZovWY#J0AkN0yJ2tAp>(9krQi$R5BW}z|@12cn81cR`HM+XCsOvB0qhXw}bDkh7B z1qqDI%vRrIHaIvoF}n1=3hBC(d14Vm-@7+IHl{eQVcK$Njl^edmsZv-Y#I^|vKxE2 l4$GN3Y|m@l%)%<*aoSuru2-0oL#@GBF`>DDosEUT8UR2#Qy>5U literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigeyes.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigeyes.gif new file mode 100644 index 0000000000000000000000000000000000000000..a281b89221c0778a7d931fbbeab3340ed3cc1412 GIT binary patch literal 234 zcmZ?wbhEHb6krfw*vtR|_4V~L(~Qrg89&&-@b8l9zw7${?pXeN==}eg@Bi1~|34)D zXZQ~UV6_a2KUp{#7?!LBsoMl7A!c}$RQ~r)389XiIG*9DWOB5(Y2eO zk!MbagM%Y07Z=wFj}HwE47^ezK9UJbj&-s#te9dMoa)BFmUL|mCxfHU9JRoAIWIRS zoa8r@Y-e!U!YL}%(_x^%U=09hT2xj5 literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigsmile.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigsmile.gif new file mode 100644 index 0000000000000000000000000000000000000000..a820c71212bac2b1c08f18e94d1186f3071a74c5 GIT binary patch literal 227 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd6Bwj9L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+R&*lI|6B{jt^oA7-Ofm)xfr*TBBDRZNSg~Q%)h0$B35^R=7celz YKd#H&U8vN!n?+%g?+SygLk$ep0JJzy_y7O^ literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigwink.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigwink.gif new file mode 100644 index 0000000000000000000000000000000000000000..84ee7483a1a5cac63f6746b184c3dcab4562fdf6 GIT binary patch literal 224 zcmZ?wbhEHb6krfw*vtR|Gt-REq#6Iar26l={=Ykx{~kL3f9CuDb@=}eiT}T5|Nm9` zpW#1PA&5}?$->FNz{8*e;(*Lx;OJ$L;2^9;Hj7SKCO9l* zoW@W$&85=lfY*F;UN2UG2QLoIXI#dV5uuQLX@2m|Zz>uM3}LHSFA1ISC~LU6J?&=J T9E%0-8fI}m_wtnxV6X-NM&?i2 literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/blue.gif b/app/Plugin/comment/Lib/nbbc/smileys/blue.gif new file mode 100644 index 0000000000000000000000000000000000000000..174db4983c268d2db7263de4998561512e3d8d6e GIT binary patch literal 233 zcmZ?wbhEHb6krfw*vtR|P8n1GGyG5O{FmMHudx4L`NV%UQ~x*5_}@AE|Acw}r!V{u zRs|&#f3k2gFfcLbfcPLY7&xXeuyTlSELd=`k%Lu)Ct<$1_s7nF6ItXjs*?TObMK95(?Ya Y-Pj?m&UeF_LFrZt*Uf*T5&{g?0B9Obng9R* literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/boggle.gif b/app/Plugin/comment/Lib/nbbc/smileys/boggle.gif new file mode 100644 index 0000000000000000000000000000000000000000..55b3da12aeba9a049483b4a1ed9ba690114fa06f GIT binary patch literal 227 zcmZ?wbhEHb6krfw*vtR|_4V~<(v1IIQvG*b|KA+Z`fq|Pr2gC)L!N4(@fsun{LxO^1GdGt}g24vI=0(7i&icBnu{Cp-8f*zc1WMtt}2nb4GVpZoAQCKi} z`N`P~d>pD=3F(cK^aB^woHA($o)xrQ=tN|}frfd;?R*Lm7ql)iu<$%lF=)s>#=x}a a)Yn&L4_$UJeR`&neSMwd#4=_k25SITqEqbv literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/cool.gif b/app/Plugin/comment/Lib/nbbc/smileys/cool.gif new file mode 100644 index 0000000000000000000000000000000000000000..35e35a8c081663328cb44398602e8be6932e11b4 GIT binary patch literal 217 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddZ4A;JA{+}A9Bkx}77-~}@Q{&_RhTKEf}zp1 zo1c+q&J6*_rha+B6qOejluxiI@n@Kv*pzyjfiY%NOXP+I#+hvVcUTgFQyp2kc)IS~ z-2CVy3tu_ciVnZ~O Oz;?Be8%Bu^4Aub7>`v7H literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/evil.gif b/app/Plugin/comment/Lib/nbbc/smileys/evil.gif new file mode 100644 index 0000000000000000000000000000000000000000..63073dd9935a3d5036d37dfa1d588f0eeed54682 GIT binary patch literal 236 zcmZ?wbhEHb6krfw*vtR|K0ZD((~Qrg8UMSa`tQ2_zdM%y9yLxf|&f`g45(jqbi3m!HyvI;XvG%Q$j zv`c{9&xXN)p^1f4fP>+K0D~hlue(;jg^fz?t;_*R5jztOo|>S_C^8|kfU&vBf>-2D zC4=L6RtG^DfmOk2OsuB4Y_eUK7KBdn7Jn!5K%t>|9UqI%*%ig=4C`Y(r8E+R(i+#Y ivPwkEp7?-)DgX1nwW|x28uzm(Z1Y|5A?r{BgEaugWmO0O literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/frown.gif b/app/Plugin/comment/Lib/nbbc/smileys/frown.gif new file mode 100644 index 0000000000000000000000000000000000000000..fb126d49ab34ebb76b7d87825cc45840ca99653c GIT binary patch literal 232 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddQyHW=L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho-+lU2Z4qIjja59JPHmD49yG@Rwg?F7#Nv^CFAB;Tu5SMW|gVZX%JlGIzd8m(jAV# zr>;Hv!9gquhRcr57STSGbK>*DLmf8Fzy3%%EIP5slHV=mM9B)LMsHcGTCS}xLRN)N OP72+%Rr^{4gEat>bvV2L literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/irritated.gif b/app/Plugin/comment/Lib/nbbc/smileys/irritated.gif new file mode 100644 index 0000000000000000000000000000000000000000..2ec3c5f65cb18a166c373aa6842c53759d214dec GIT binary patch literal 232 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddQyHW=L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1q#9+bm^V8+*`4|KiJPc%Xu=QJ;CA#Pklf`x`n}h`oZmki#ECvU*c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(;1{WL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHc7#bM4g0v(U9F`pGR25{gIBDd-G*N(2M58k3AQPK*w46ae zLHbb^J8_+Wg5r#eGdx0#L_I^){hK4C!$da}tA#NzwmF4FBs>piaOTbkIk7eSAOjnZ eh=g|lL*u53)0ggAOl*i}vK0Pwbe4evgEau!4^8_3 literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/lookleft.gif b/app/Plugin/comment/Lib/nbbc/smileys/lookleft.gif new file mode 100644 index 0000000000000000000000000000000000000000..c92fa5c0f3195da98d2585c2c5ffc8dfab5f2657 GIT binary patch literal 225 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddeGJkZA{+}A9Bkx}77-~}@Q{&_RhTKEV?ol< zHUV}&n+AnO_kIooCWRA*jZKr)gc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd6Bwj9L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!&2Ea0)s!PUmD|NLb*o+=H1le^$tb z1uL4SvNzm1GBfDu`33s6O@D3#tYlayryJzU6`0sKn~jlWMU=zhL#xesSHwmy&~{{G Z*>~#itFp%~Ock70d@{^)4mB`X0|3tyQGfsd literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/neutral.gif b/app/Plugin/comment/Lib/nbbc/smileys/neutral.gif new file mode 100644 index 0000000000000000000000000000000000000000..19828c69c62d7944dfd8d92de48ac386c2744c9c GIT binary patch literal 225 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddeGJkZA{+}A9Bkx}77-~}@Q{&_RhTKEf}zp1 zo1c+q&J6*_rha+B6qSqxNldI7focLP3zIxqSjBZTb{Hu)FzF{7EzrE=;5bK3^hC^p z1L6$@1!vclIW)4=qngW4_!|g2mS|tqv ziH>c;vJ3(S2@VbYj83ksB(SU5#igv?|(=+Vf)w;*K0f;3-NUC+EJGY=>>v+)(~ zD#>VA?$72JEvIm5^0Kq6PScq*x{RKBO;umYrNO0;%Cwq6K&HZS>g$H}%!lL*9D*KS oTf`{CX0hR$st+S8&x^a+2l7t#vNDNF+%#K!tewx8jfKG)0H$kLp#T5? literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/sleepy.gif b/app/Plugin/comment/Lib/nbbc/smileys/sleepy.gif new file mode 100644 index 0000000000000000000000000000000000000000..7c0bc0295945b3c6d812f7329fa1b1a8422cffe0 GIT binary patch literal 217 zcmZ?wbhEHb6krfw*vtR|XVQ%ST~hsbUH{)5%YP4@|3CBn|2q8ths6J1v;Q;v2P;7k zia%L685p=2bU;ENGZ;A97(_W_JOUINni;s1Y&;Ae9%yFd^5A)JVd23JR-O!#0EdH4 z{Sv$~5|s^$otPx~6(TwxEOuuQbXD@0z~JcF&A=vO!eGqk$-u%PA=7Zl!J9!l_Lc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(-@>VL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+RPX;+hHHHfxRxm7JV2hA(_}akO!NtwM8(`pYl}So8?r7$MwYQlP eIJp!QwynFdLt0(vhBrgftrjj`Hx~&325SIpHct)! literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/smile3.gif b/app/Plugin/comment/Lib/nbbc/smileys/smile3.gif new file mode 100644 index 0000000000000000000000000000000000000000..43826af3ce2119ec250a73b03c2ad1531c0d4f32 GIT binary patch literal 238 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddvl*m0L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+RPX;+hHHHfxRxr$AU{l}_U^vLIicLXFr@`3a+ImJl35^AT4@2j$ kJvybUFl_+?Q_AN_Yj+naHST9o*yg+9LV`;RBO`+~01rD-FNz{8*e;(*Lx;F!W7$sxkAV8Ovg4oMM_gar#38Civy1PT@`I@%?` z?q|ZFaDb78gO7#bg8;)pCSG?nj|&@>+*^70MM4~kpE5GDDlj={3O6*dvgL~@1WaJ? zn!uT%i9#Q4ZET2UlH}x) a*s$&0tsPS8d^fBe(hu#46lP;#um%9jQB2AJ literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/star.gif b/app/Plugin/comment/Lib/nbbc/smileys/star.gif new file mode 100644 index 0000000000000000000000000000000000000000..a764ae8d62094d00b987cd9685260db4b0cf2d79 GIT binary patch literal 208 zcmZ?wbhEHb6krfw*vtR|wYAd!{xN+2%J}9L%fp8pH?HyQ-Yxx~;s5{twg3O`22x;E zP(tx13nv2u2ZIiX4>E&+qn1ICL#Bg);b1d6Ba1Skr*uplFFAs@R& wtX0BthgJ(#kExjx6&hwUiLp)aOjz#3#LZjA!(gU#W>VOBzmi#D2@VX_0DNRYt^fc4 literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/surprise.gif b/app/Plugin/comment/Lib/nbbc/smileys/surprise.gif new file mode 100644 index 0000000000000000000000000000000000000000..541adf8ffbc25dfb39452fde6a15f7b373e4da08 GIT binary patch literal 225 zcmZ?wbhEHb6krfw*vtR|XVQ%ST~hsbUH{)5%YP4@|3CBn|2q8ths6J1v;Q;v2LiAv zD53b1g_D7Si$Mp(2bsaZ(Z?XlA;Pg>!NEojQ4yYi1&NIetintQ5eFJvyZIS;=0qHL z(8$bYETqDa(0H;*O`PGy3IhhusceDkS~wC8oStFCJzc8vy|L$1+d+7ZCneYGC;r~CSq@|tt z&+vcd%>QT3{09QXpDdgV3_J`vAe|sH7&vA!NOFj9ELh;!#LUSgP_W=3BO|LggT#&v zicQ_({0tce6CIB-a|m^aL|$-o>}S)_itsEt$lS>tEEco!zyoG>8AcHUh6hRwv$zBp z3K}*)@SDJyv5sZK0md_n6{l)>BwWbwTB0m$@nok!R#P`KgNX^l#pK4tj9hFI(F>BV k&t-dbN;hEI0tTk^&-2!L7b-UHXHh8gS+POuVFQCT0JC#gd;kCd literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/teeth.gif b/app/Plugin/comment/Lib/nbbc/smileys/teeth.gif new file mode 100644 index 0000000000000000000000000000000000000000..86eff8a86cb715c75d257491b7a95845878eb839 GIT binary patch literal 208 zcmZ?wbhEHb6krfw*vtR||1PQiyRQH5j^)3H&i|kJ{(l|*|3l*cui5_@{sRG634&1k z$->FNz{#Kk5(1gQz){N}%pt|&4@Ul=)}OxA!2f1;{w+v zW(E-hh6fIwlcXHeWGWdLo0!?O+I$!s8V=4jPrt^pz=84nJbOc?1)0Jb{_`D5#ay$l uJoi~-Q?GQ_a>Fv;m8`5c_GlSOjf||qObHbXjjrAN zj68F02sk$N%L}HcWGqNxV$}##6IfZ8Q0uw{$*s*dj7?f!rWMtVj aX>a6#x5qkJJ_$8Mm@RZ#!^zCVU=08s2v5-f literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/wink.gif b/app/Plugin/comment/Lib/nbbc/smileys/wink.gif new file mode 100644 index 0000000000000000000000000000000000000000..d8563fedb81ce08762e913a06397b61eb441764a GIT binary patch literal 229 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddlNh8qL^u{KIM~P`Eh18|;2|R;t1weS$AYAz zZ366mHVq1m?)@AFObRCq8=EGp3%8gU6e%|~aVO1-;S_9O>@$#*h>2W~&^TL8^hnGC zho_9w80x0ER2m)dns2UM_k}~@z{OTCT_p~N1q&N{Jf}*@JlLx4$jDkJ6>)l-!?DdQ amrnWq4twmvRKa=0C*%49j}}Ho25SH~JX4YY literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/wink3.gif b/app/Plugin/comment/Lib/nbbc/smileys/wink3.gif new file mode 100644 index 0000000000000000000000000000000000000000..76b0d18b6c6a02e09dedbc2a16858ac43eb42d6e GIT binary patch literal 234 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(;1{WL^u{KIM~P`Eh18|;2|R;t1weS$AYAz zZ366mHVq1m?)@AFObRCq8=EGp3%8gU6e%|~aVO1-;S_9O>@$#*h>2W~&^TL8^hnGC zho_9w80x0ER2m)dns2UMCG$Ygp#^ug|X;n9cjYsc0-X;J>AS fSLDQrX$`k`=e^9Dc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddlNqErL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho-v-u(*Mau!+)83!4d?U-h81tvB!Mf3A;1YBrf+`zK>k;&^t@MSJ`71rV37nD>Ei6IM~6#%*0>~0O8hC0RR91 literal 0 HcmV?d00001 diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php new file mode 100644 index 00000000..71345759 --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php @@ -0,0 +1,158 @@ +check_email_address('test@example.org')) { + // // Email address is technically valid + // } + // + // * Note: This version is slightly modified for PHP4 compatibility + // and to work well with NBBC. You can get the original at + // the URL shown above. + + class BBCodeEmailAddressValidator { + + // Check email address validity + // @param strEmailAddress Email address to be checked + // @return True if email is valid, false if not + function check_email_address($strEmailAddress) { + + // If magic quotes is "on", email addresses with quote marks will + // fail validation because of added escape characters. Uncommenting + // the next three lines will allow for this issue. + //if (get_magic_quotes_gpc()) { + // $strEmailAddress = stripslashes($strEmailAddress); + //} + + // Control characters are not allowed + if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { + return false; + } + + // Split it into sections using last instance of "@" + $intAtSymbol = strrpos($strEmailAddress, '@'); + if ($intAtSymbol === false) { + // No "@" symbol in email. + return false; + } + $arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); + $arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); + + // Count the "@" symbols. Only one is allowed, except where + // contained in quote marks in the local part. Quickest way to + // check this is to remove anything in quotes. + $arrTempAddress[0] = preg_replace('/"[^"]+"/' + ,'' + ,$arrEmailAddress[0]); + $arrTempAddress[1] = $arrEmailAddress[1]; + $strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; + // Then check - should be no "@" symbols. + if (strrpos($strTempAddress, '@') !== false) { + // "@" symbol found + return false; + } + + // Check local portion + if (!$this->check_local_portion($arrEmailAddress[0])) { + return false; + } + + // Check domain portion + if (!$this->check_domain_portion($arrEmailAddress[1])) { + return false; + } + + // If we're still here, all checks above passed. Email is valid. + return true; + + } + + // Checks email section before "@" symbol for validity + // @param strLocalPortion Text to be checked + // @return True if local portion is valid, false if not + function check_local_portion($strLocalPortion) { + // Local portion can only be from 1 to 64 characters, inclusive. + // Please note that servers are encouraged to accept longer local + // parts than 64 characters. + if (!$this->check_text_length($strLocalPortion, 1, 64)) { + return false; + } + // Local portion must be: + // 1) a dot-atom (strings separated by periods) + // 2) a quoted string + // 3) an obsolete format string (combination of the above) + $arrLocalPortion = explode('.', $strLocalPortion); + for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { + if (!preg_match('.^(' + . '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' + . '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' + .'|' + . '("[^\\\"]{0,62}")' + .')$.' + ,$arrLocalPortion[$i])) { + return false; + } + } + return true; + } + + // Checks email section after "@" symbol for validity + // @param strDomainPortion Text to be checked + // @return True if domain portion is valid, false if not + function check_domain_portion($strDomainPortion) { + // Total domain can only be from 1 to 255 characters, inclusive + if (!$this->check_text_length($strDomainPortion, 1, 255)) { + return false; + } + // Check if domain is IP, possibly enclosed in square brackets. + if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' + .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' + ,$strDomainPortion) || + preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' + .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' + ,$strDomainPortion)) { + return true; + } else { + $arrDomainPortion = explode('.', $strDomainPortion); + if (sizeof($arrDomainPortion) < 2) { + return false; // Not enough parts to domain + } + for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { + // Each portion must be between 1 and 63 characters, inclusive + if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { + return false; + } + if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' + .'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { + return false; + } + } + } + return true; + } + + // Check given text length is between defined bounds + // @param strText Text to be checked + // @param intMinimum Minimum acceptable length + // @param intMaximum Maximum acceptable length + // @return True if string is within bounds (inclusive), false if not + function check_text_length($strText, $intMinimum, $intMaximum) { + // Minimum and maximum are both inclusive + $intTextLength = strlen($strText); + if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { + return false; + } else { + return true; + } + } + + } + +?> \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php new file mode 100644 index 00000000..9d41beb5 --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php @@ -0,0 +1,591 @@ +NextToken is called, the next token is returned until it returns + // BBCODE_EOI at the end of the input. + // + //----------------------------------------------------------------------------- + + class BBCodeLexer { + var $token; // Return token type: One of the BBCODE_* constants. + var $text; // Actual exact, original text of token. + var $tag; // If token is a tag, this is the decoded array version. + + var $state; // Next state of the lexer's state machine: text, or tag/ws/nl + var $input; // The input string, split into an array of tokens. + var $ptr; // Read pointer into the input array. + var $unget; // Whether to "unget" the last token. + + var $verbatim; // In verbatim mode, we return all input, unparsed, including comments. + var $debug; // In debug mode, we dump decoded tags when we find them. + + var $tagmarker; // Which kind of tag marker we're using: "[", "<", "(", or "{" + var $end_tagmarker; // The ending tag marker: "]", ">", "(", or "{" + var $pat_main; // Main tag-matching pattern. + var $pat_comment; // Pattern for matching comments. + var $pat_comment2; // Pattern for matching comments. + var $pat_wiki; // Pattern for matching wiki-links. + + function BBCodeLexer($string, $tagmarker = '[') { + // First thing we do is to split the input string into tuples of + // text and tags. This will make it easy to tokenize. We define a tag as + // anything starting with a [, ending with a ], and containing no [ or ] in + // between unless surrounded by "" or '', and containing no newlines. + // We also separate out whitespace and newlines. + + // Choose a tag marker based on the possible tag markers. + $regex_beginmarkers = Array( '[' => '\[', '<' => '<', '{' => '\{', '(' => '\(' ); + $regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); + $endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); + if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; + $e = $regex_endmarkers[$tagmarker]; + $b = $regex_beginmarkers[$tagmarker]; + $this->tagmarker = $tagmarker; + $this->end_tagmarker = $endmarkers[$tagmarker]; + + // $this->input will be an array of tokens, with the special property that + // the elements strictly alternate between plain text and tags/whitespace/newlines, + // and that tags always have *two* entries per tag. The first element will + // always be plain text. Note that the regexes below make VERY heavy use of + // PCRE regex-syntax extensions, so don't even try to modify them unless you + // know how things like (?!) and (?:) and (?=) work. We use the /x modifier + // here to make this a *lot* more legible and debuggable. + $this->pat_main = "/( " + // Match tags, as long as they do not start with [-- or [' or [!-- or [rem or [[. + // Tags may contain "quoted" or 'quoted' sections that may contain [ or ] characters. + // Tags may not contain newlines. + . "{$b}" + . "(?! -- | ' | !-- | {$b}{$b} )" + . "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" + . "{$e}" + + // Match wiki-links, which are of the form [[...]] or [[...|...]]. Unlike + // tags, wiki-links treat " and ' marks as normal input characters; but they + // still may not contain newlines. + . "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" + + // Match single-line comments, which start with [-- or [' or [rem . + . "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" + + // Match multi-line comments, which start with [!-- and end with --] and contain + // no --] in between. + . "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" + + // Match five or more hyphens as a special token, which gets returned as a [rule] tag. + . "| -----+" + + // Match newlines, in all four possible forms. + . "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" + + // Match whitespace, but only if it butts up against a newline, rule, or + // bracket on at least one end. + . "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" + . "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" + + . " )/Dx"; + + $this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); + + // Patterns for matching specific types of tokens during lexing. + $this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; + $this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; + $this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; + + // Current lexing state. + $this->ptr = 0; + $this->unget = false; + $this->state = BBCODE_LEXSTATE_TEXT; + $this->verbatim = false; + + // Return values. + $this->token = BBCODE_EOI; + $this->tag = false; + $this->text = ""; + } + + // Compute how many non-tag characters there are in the input, give or take a few. + // This is optimized for speed, not accuracy, so it'll get some stuff like + // horizontal rules and weird whitespace characters wrong, but it's only supposed + // to provide a rough quick guess, not a hard fact. + function GuessTextLength() { + $length = 0; + $ptr = 0; + $state = BBCODE_LEXSTATE_TEXT; + + // Loop until we find a valid (nonempty) token. + while ($ptr < count($this->input)) { + $text = $this->input[$ptr++]; + + if ($state == BBCODE_LEXSTATE_TEXT) { + $state = BBCODE_LEXSTATE_TAG; + $length += strlen($text); + } + else { + switch (ord(substr($this->text, 0, 1))) { + case 10: + case 13: + $state = BBCODE_LEXSTATE_TEXT; + $length++; + break; + default: + $state = BBCODE_LEXSTATE_TEXT; + $length += strlen($text); + break; + case 40: + case 60: + case 91: + case 123: + $state = BBCODE_LEXSTATE_TEXT; + break; + } + } + } + + return $length; + } + + // Return the type of the next token, either BBCODE_TAG or BBCODE_TEXT or + // BBCODE_EOI. This stores the content of this token into $this->text, the + // type of this token in $this->token, and possibly an array into $this->tag. + // + // If this is a BBCODE_TAG token, $this->tag will be an array computed from + // the tag's contents, like this: + // Array( + // '_name' => tag_name, + // '_end' => true if this is an end tag (i.e., the name starts with a /) + // '_default' => default value (for example, in [url=foo], this is "foo"). + // ... + // ...all other key => value parameters given in the tag... + // ... + // ) + function NextToken() { + + // Handle ungets; if the last token has been "ungotten", just return it again. + if ($this->unget) { + $this->unget = false; + return $this->token; + } + + // Loop until we find a valid (nonempty) token. + while (true) { + + // Did we run out of tokens in the input? + if ($this->ptr >= count($this->input)) { + $this->text = ""; + $this->tag = false; + return $this->token = BBCODE_EOI; + } + + // Inhale one token, sanitizing away any weird control characters. We + // allow \t, \r, and \n to pass through, but that's it. + $this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", + $this->input[$this->ptr++]); + + if ($this->verbatim) { + + // In verbatim mode, we return *everything* as plain text or whitespace. + $this->tag = false; + if ($this->state == BBCODE_LEXSTATE_TEXT) { + $this->state = BBCODE_LEXSTATE_TAG; + $token_type = BBCODE_TEXT; + } + else { + // This must be either whitespace, a newline, or a tag. + $this->state = BBCODE_LEXSTATE_TEXT; + switch (ord(substr($this->text, 0, 1))) { + case 10: + case 13: + // Newline. + $token_type = BBCODE_NL; + break; + default: + // Whitespace. + $token_type = BBCODE_WS; + break; + case 45: + case 40: + case 60: + case 91: + case 123: + // Tag or comment. + $token_type = BBCODE_TEXT; + break; + } + } + + if (strlen($this->text) > 0) + return $this->token = $token_type; + } + else if ($this->state == BBCODE_LEXSTATE_TEXT) { + // Next up is plain text, but only return it if it's nonempty. + $this->state = BBCODE_LEXSTATE_TAG; + $this->tag = false; + if (strlen($this->text) > 0) + return $this->token = BBCODE_TEXT; + } + else { + // This must be either whitespace, a newline, or a tag. + switch (ord(substr($this->text, 0, 1))) { + case 10: + case 13: + // Newline. + $this->tag = false; + $this->state = BBCODE_LEXSTATE_TEXT; + return $this->token = BBCODE_NL; + case 45: + // A rule made of hyphens; return it as a [rule] tag. + if (preg_match("/^-----/", $this->text)) { + $this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); + $this->state = BBCODE_LEXSTATE_TEXT; + return $this->token = BBCODE_TAG; + } + else { + $this->tag = false; + $this->state = BBCODE_LEXSTATE_TEXT; + if (strlen($this->text) > 0) + return $this->token = BBCODE_TEXT; + continue; + } + default: + // Whitespace. + $this->tag = false; + $this->state = BBCODE_LEXSTATE_TEXT; + return $this->token = BBCODE_WS; + case 40: + case 60: + case 91: + case 123: + // Tag or comment. This is the most complicated one, because it + // needs to be parsed into its component pieces. + + // See if this is a comment; if so, skip it. + if (preg_match($this->pat_comment, $this->text)) { + // This is a comment, not a tag, so treat it like it doesn't exist. + $this->state = BBCODE_LEXSTATE_TEXT; + continue; + } + if (preg_match($this->pat_comment2, $this->text)) { + // This is a comment, not a tag, so treat it like it doesn't exist. + $this->state = BBCODE_LEXSTATE_TEXT; + continue; + } + + // See if this is a [[wiki link]]; if so, convert it into a [wiki="" title=""] tag. + if (preg_match($this->pat_wiki, $this->text, $matches)) { + $this->tag = Array('_name' => 'wiki', '_endtag' => false, + '_default' => @$matches[1], 'title' => @$matches[2]); + $this->state = BBCODE_LEXSTATE_TEXT; + return $this->token = BBCODE_TAG; + } + + // Not a comment, so parse it like a tag. + $this->tag = $this->Internal_DecodeTag($this->text); + $this->state = BBCODE_LEXSTATE_TEXT; + return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); + } + } + } + } + + // Ungets the last token read so that a subsequent call to NextToken() will + // return it. Note that UngetToken() does not switch states when you switch + // between verbatim mode and standard mode: For example, if you read a tag, + // unget the tag, switch to verbatim mode, and then get the next token, you'll + // get back a BBCODE_TAG --- exactly what you ungot, not a BBCODE_TEXT token. + function UngetToken() { + if ($this->token !== BBCODE_EOI) + $this->unget = true; + } + + // Peek at the next token, but don't remove it. + function PeekToken() { + $result = $this->NextToken(); + if ($this->token !== BBCODE_EOI) + $this->unget = true; + return $result; + } + + // Save the state of this lexer so it can be restored later. The return + // value from this should be considered opaque. Because PHP uses copy-on-write + // references, the total cost of the returned state is relatively small, and + // the running time of this function (and RestoreState) is very fast. + function SaveState() { + return Array( + 'token' => $this->token, + 'text' => $this->text, + 'tag' => $this->tag, + 'state' => $this->state, + 'input' => $this->input, + 'ptr' => $this->ptr, + 'unget' => $this->unget, + 'verbatim' => $this->verbatim + ); + } + + // Restore the state of this lexer from a saved previous state. + function RestoreState($state) { + if (!is_array($state)) return; + $this->token = @$state['token']; + $this->text = @$state['text']; + $this->tag = @$state['tag']; + $this->state = @$state['state']; + $this->input = @$state['input']; + $this->ptr = @$state['ptr']; + $this->unget = @$state['unget']; + $this->verbatim = @$state['verbatim']; + } + + // Given a string, if it's surrounded by "quotes" or 'quotes', remove them. + function Internal_StripQuotes($string) { + if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) + return $matches[1]; + else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) + return $matches[1]; + else return $string; + } + + // Given a tokenized piece of a tag, decide what type of token it is. Our + // return values are: + // -1 End-of-input (EOI). + // '=' Token is an = sign. + // ' ' Token is whitespace. + // '"' Token is quoted text. + // 'A' Token is unquoted text. + function Internal_ClassifyPiece($ptr, $pieces) { + if ($ptr >= count($pieces)) return -1; // EOI. + $piece = $pieces[$ptr]; + if ($piece == '=') return '='; + else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; + else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; + else return 'A'; + } + + // Given a string containing a complete [tag] (including its brackets), break + // it down into its components and return them as an array. + function Internal_DecodeTag($tag) { + + if ($this->debug) { + print "Lexer::InternalDecodeTag: input: " . htmlspecialchars($tag) . "
    \n"; + } + + // Create the initial result object. + $result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', + '_hasend' => false, '_end' => false, '_default' => false); + + // Strip off the [brackets] around the tag, leaving just its content. + $tag = substr($tag, 1, strlen($tag)-2); + + // The starting bracket *must* be followed by a non-whitespace character. + $ch = ord(substr($tag, 0, 1)); + if ($ch >= 0 && $ch <= 32) return $result; + + // Break it apart into words, quoted text, whitespace, and equal signs. + $pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", + $tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + $ptr = 0; + + // Handle malformed (empty) tags correctly. + if (count($pieces) < 1) return $result; + + // The first piece should be the tag name, whatever it is. If it starts with a / + // we remove the / and mark it as an end tag. + if (@substr($pieces[$ptr], 0, 1) == '/') { + $result['_name'] = strtolower(substr($pieces[$ptr++], 1)); + $result['_end'] = true; + } + else { + $result['_name'] = strtolower($pieces[$ptr++]); + $result['_end'] = false; + } + + // Skip whitespace after the tag name. + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') + $ptr++; + + $params = Array(); + + // If the next piece is an equal sign, then the tag's default value follows. + if ($type != '=') { + $result['_default'] = false; + $params[] = Array('key' => '', 'value' => ''); + } + else { + $ptr++; + + // Skip whitespace after the initial equal-sign. + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') + $ptr++; + + // Examine the next (real) piece, and see if it's quoted; if not, we need to + // use heuristics to guess where the default value begins and ends. + if ($type == "\"") + $value = $this->Internal_StripQuotes($pieces[$ptr++]); + else { + // Collect pieces going forward until we reach an = sign or the end of the + // tag; then rewind before whatever comes before the = sign, and everything + // between here and there becomes the default value. This allows tags like + // [font=Times New Roman size=4] to make sense even though the font name is + // not quoted. Note, however, that there's a special initial case, where + // any equal-signs before whitespace are considered to be part of the parameter + // as well; this allows an ugly tag like [url=http://foo?bar=baz target=my_window] + // to behave in a way that makes (tolerable) sense. + $after_space = false; + $start = $ptr; + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { + if ($type == ' ') $after_space = true; + if ($type == '=' && $after_space) break; + $ptr++; + } + if ($type == -1) $ptr--; + + // We've now found the first (appropriate) equal-sign after the start of the + // default value. (In the example above, that's the "=" after "target".) We + // now have to rewind back to the last whitespace to find where the default + // value ended. + if ($type == '=') { + // Rewind before = sign. + $ptr--; + // Rewind before any whitespace before = sign. + while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') + $ptr--; + // Rewind before any text elements before that. + while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') + $ptr--; + } + + // The default value is everything from $start to $ptr, inclusive. + $value = ""; + for (; $start <= $ptr; $start++) { + if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') + $value .= " "; + else $value .= $this->Internal_StripQuotes($pieces[$start]); + } + $value = trim($value); + + $ptr++; + } + + $result['_default'] = $value; + $params[] = Array('key' => '', 'value' => $value); + } + + // The rest of the tag is composed of either floating keys or key=value pairs, so walk through + // the tag and collect them all. Again, we have the nasty special case where an equal sign + // in a parameter but before whitespace counts as part of that parameter. + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { + + // Skip whitespace before the next key name. + while ($type == ' ') { + $ptr++; + $type = $this->Internal_ClassifyPiece($ptr, $pieces); + } + + // Decode the key name. + if ($type == 'A' || $type == '"') + $key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); + else if ($type == '=') { + $ptr++; + continue; + } + else if ($type == -1) break; + + // Skip whitespace after the key name. + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') + $ptr++; + + // If an equal-sign follows, we need to collect a value. Otherwise, we + // take the key itself as the value. + if ($type != '=') + $value = $this->Internal_StripQuotes($key); + else { + $ptr++; + // Skip whitespace after the equal sign. + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') + $ptr++; + if ($type == '"') { + // If we get a quoted value, take that as the only value. + $value = $this->Internal_StripQuotes($pieces[$ptr++]); + } + else if ($type != -1) { + // If we get a non-quoted value, consume non-quoted values + // until we reach whitespace. + $value = $pieces[$ptr++]; + while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 + && $type != ' ') + $value .= $pieces[$ptr++]; + } + else $value = ""; + } + + // Record this in the associative array if it's a legal public identifier name. + // Legal *public* identifier names must *not* begin with an underscore. + if (substr($key, 0, 1) != '_') + $result[$key] = $value; + + // Record this in the parameter list always. + $params[] = Array('key' => $key, 'value' => $value); + } + + // Add the parameter list as a member of the associative array. + $result['_params'] = $params; + + if ($this->debug) { + // In debugging modes, output the tag as we collected it. + print "Lexer::InternalDecodeTag: output: "; + ob_start(); + print_r($result); + $output = ob_get_clean(); + print htmlspecialchars($output) . "
    \n"; + } + + // Save the resulting parameters, and return the whole shebang. + return $result; + } + } + +?> \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php new file mode 100644 index 00000000..af0bcd34 --- /dev/null +++ b/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php @@ -0,0 +1,655 @@ +AddRule(...); + // $bbcode->AddSmiley(...); + // + //----------------------------------------------------------------------------- + + class BBCodeLibrary { + + //----------------------------------------------------------------------------- + // Standard library of smiley definitions. + + var $default_smileys = Array( + ':)' => 'smile.gif', ':-)' => 'smile.gif', + '=)' => 'smile.gif', '=-)' => 'smile.gif', + ':(' => 'frown.gif', ':-(' => 'frown.gif', + '=(' => 'frown.gif', '=-(' => 'frown.gif', + ':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', + '=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', + '>:('=> 'angry.gif', '>:-('=> 'angry.gif', + '>=('=> 'angry.gif', '>=-('=> 'angry.gif', + 'D:' => 'angry.gif', 'D-:' => 'angry.gif', + 'D=' => 'angry.gif', 'D-=' => 'angry.gif', + '>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', + '>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', + '>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', + '>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', + '>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', + '>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', + 'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', + 'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', + ':O' => 'surprise.gif', ':-O' => 'surprise.gif', + '=O' => 'surprise.gif', '=-O' => 'surprise.gif', + ':?' => 'confuse.gif', ':-?' => 'confuse.gif', + '=?' => 'confuse.gif', '=-?' => 'confuse.gif', + ':s' => 'worry.gif', ':-S' => 'worry.gif', + '=s' => 'worry.gif', '=-S' => 'worry.gif', + ':|' => 'neutral.gif', ':-|' => 'neutral.gif', + '=|' => 'neutral.gif', '=-|' => 'neutral.gif', + ':I' => 'neutral.gif', ':-I' => 'neutral.gif', + '=I' => 'neutral.gif', '=-I' => 'neutral.gif', + ':/' => 'irritated.gif', ':-/' => 'irritated.gif', + '=/' => 'irritated.gif', '=-/' => 'irritated.gif', + ':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', + '=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', + ':P' => 'tongue.gif', ':-P' => 'tongue.gif', + '=P' => 'tongue.gif', '=-P' => 'tongue.gif', + 'X-P' => 'tongue.gif', + '8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', + 'B)' => 'cool.gif', 'B-)' => 'cool.gif', + ';)' => 'wink.gif', ';-)' => 'wink.gif', + ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', + '^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', + '>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', + '<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', + 'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', + ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', + ':3' => 'smile3.gif', ':-3' => 'smile3.gif', + '=3' => 'smile3.gif', '=-3' => 'smile3.gif', + ';3' => 'wink3.gif', ';-3' => 'wink3.gif', + '' => 'teeth.gif', '' => 'teeth.gif', + 'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', + ':blue:' => 'blue.gif', + ':zzz:' => 'sleepy.gif', + '<3' => 'heart.gif', + ':star:' => 'star.gif', + ); + + //----------------------------------------------------------------------------- + // Standard rules for what to do when a BBCode tag is encountered. + + var $default_tag_rules = Array( + + 'b' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'plain_start' => "", + 'plain_end' => "", + ), + 'i' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'plain_start' => "", + 'plain_end' => "", + ), + 'u' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'plain_start' => "", + 'plain_end' => "", + ), + 's' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'plain_start' => "", + 'plain_end' => "", + ), + + 'font' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), + 'method' => 'DoFont', + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'color' => Array( + 'mode' => BBCODE_MODE_ENHANCED, + 'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), + 'template' => '{$_content/v}', + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'size' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'allow' => Array('_default' => '/^[0-9.]+$/D'), + 'method' => 'DoSize', + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'sup' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'sub' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'spoiler' => Array( + 'simple_start' => "", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + 'acronym' => Array( + 'mode' => BBCODE_MODE_ENHANCED, + 'template' => '{$_content/v}', + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + ), + + 'url' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => 'DoURL', + 'class' => 'link', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), + 'content' => BBCODE_REQUIRED, + 'plain_start' => "", + 'plain_end' => "", + 'plain_content' => Array('_content', '_default'), + 'plain_link' => Array('_default', '_content'), + ), + 'email' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => 'DoEmail', + 'class' => 'link', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), + 'content' => BBCODE_REQUIRED, + 'plain_start' => "", + 'plain_end' => "", + 'plain_content' => Array('_content', '_default'), + 'plain_link' => Array('_default', '_content'), + ), + 'wiki' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => "DoWiki", + 'class' => 'link', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), + 'end_tag' => BBCODE_PROHIBIT, + 'content' => BBCODE_PROHIBIT, + 'plain_start' => "[", + 'plain_end' => "]", + 'plain_content' => Array('title', '_default'), + 'plain_link' => Array('_default', '_content'), + ), + + 'img' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => "DoImage", + 'class' => 'image', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'end_tag' => BBCODE_REQUIRED, + 'content' => BBCODE_REQUIRED, + 'plain_start' => "[image]", + 'plain_content' => Array(), + ), + 'rule' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => "DoRule", + 'class' => 'block', + 'allow_in' => Array('listitem', 'block', 'columns'), + 'end_tag' => BBCODE_PROHIBIT, + 'content' => BBCODE_PROHIBIT, + 'before_tag' => "sns", + 'after_tag' => "sns", + 'plain_start' => "\n-----\n", + 'plain_end' => "", + 'plain_content' => Array(), + ), + 'br' => Array( + 'mode' => BBCODE_MODE_SIMPLE, + 'simple_start' => "
    \n", + 'simple_end' => "", + 'class' => 'inline', + 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), + 'end_tag' => BBCODE_PROHIBIT, + 'content' => BBCODE_PROHIBIT, + 'before_tag' => "s", + 'after_tag' => "s", + 'plain_start' => "\n", + 'plain_end' => "", + 'plain_content' => Array(), + ), + + 'left' => Array( + 'simple_start' => "\n
    \n", + 'simple_end' => "\n
    \n", + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + 'right' => Array( + 'simple_start' => "\n
    \n", + 'simple_end' => "\n
    \n", + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + 'center' => Array( + 'simple_start' => "\n
    \n", + 'simple_end' => "\n
    \n", + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + 'indent' => Array( + 'simple_start' => "\n
    \n", + 'simple_end' => "\n
    \n", + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + + 'columns' => Array( + 'simple_start' => "\n
    \n", + 'simple_end' => "\n
    \n", + 'class' => 'columns', + 'allow_in' => Array('listitem', 'block', 'columns'), + 'end_tag' => BBCODE_REQUIRED, + 'content' => BBCODE_REQUIRED, + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + 'nextcol' => Array( + 'simple_start' => "\n\n", + 'class' => 'nextcol', + 'allow_in' => Array('columns'), + 'end_tag' => BBCODE_PROHIBIT, + 'content' => BBCODE_PROHIBIT, + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "", + ), + + 'code' => Array( + 'mode' => BBCODE_MODE_ENHANCED, + 'template' => "\n
    \n
    Code:
    \n
    {\$_content/v}
    \n
    \n", + 'class' => 'code', + 'allow_in' => Array('listitem', 'block', 'columns'), + 'content' => BBCODE_VERBATIM, + 'before_tag' => "sns", + 'after_tag' => "sn", + 'before_endtag' => "sn", + 'after_endtag' => "sns", + 'plain_start' => "\nCode:\n", + 'plain_end' => "\n", + ), + 'quote' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => "DoQuote", + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\nQuote:\n", + 'plain_end' => "\n", + ), + + 'list' => Array( + 'mode' => BBCODE_MODE_LIBRARY, + 'method' => 'DoList', + 'class' => 'list', + 'allow_in' => Array('listitem', 'block', 'columns'), + 'before_tag' => "sns", + 'after_tag' => "sns", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n", + 'plain_end' => "\n", + ), + '*' => Array( + 'simple_start' => "
  • ", + 'simple_end' => "
  • \n", + 'class' => 'listitem', + 'allow_in' => Array('list'), + 'end_tag' => BBCODE_OPTIONAL, + 'before_tag' => "s", + 'after_tag' => "s", + 'before_endtag' => "sns", + 'after_endtag' => "sns", + 'plain_start' => "\n * ", + 'plain_end' => "\n", + ), + ); + + //----------------------------------------------------------------------------- + // Standard library of BBCode formatting routines. + + // Format a [url] tag by producing an ... element. + // The URL only allows http, https, mailto, and ftp protocols for safety. + function DoURL($bbcode, $action, $name, $default, $params, $content) { + // We can't check this with BBCODE_CHECK because we may have no URL before the content + // has been processed. + if ($action == BBCODE_CHECK) return true; + + $url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); + if ($bbcode->IsValidURL($url)) { + if ($bbcode->debug) + print "ISVALIDURL
    "; + if ($bbcode->url_targetable !== false && isset($params['target'])) + $target = " target=\"" . htmlspecialchars($params['target']) . "\""; + else $target = ""; + if ($bbcode->url_target !== false) + if (!($bbcode->url_targetable == 'override' && isset($params['target']))) + $target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; + return '' . $content . ''; + } + else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); + } + + // Format an [email] tag by producing an ... element. + // The e-mail address must be a valid address including at least a '@' and a valid domain + // name or IPv4 or IPv6 address after the '@'. + function DoEmail($bbcode, $action, $name, $default, $params, $content) { + // We can't check this with BBCODE_CHECK because we may have no URL before the content + // has been processed. + if ($action == BBCODE_CHECK) return true; + + $email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); + if ($bbcode->IsValidEmail($email)) + return '' . $content . ''; + else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); + } + + // Format a [size] tag by producing a with a style with a different font-size. + function DoSize($bbcode, $action, $name, $default, $params, $content) { + switch ($default) { + case '0': $size = '.5em'; break; + case '1': $size = '.67em'; break; + case '2': $size = '.83em'; break; + default: + case '3': $size = '1.0em'; break; + case '4': $size = '1.17em'; break; + case '5': $size = '1.5em'; break; + case '6': $size = '2.0em'; break; + case '7': $size = '2.5em'; break; + } + return "$content"; + } + + // Format a [font] tag by producing a with a style with a different font-family. + // This is complicated by the fact that we have to recognize the five special font + // names and quote all the others. + function DoFont($bbcode, $action, $name, $default, $params, $content) { + $fonts = explode(",", $default); + $result = ""; + $special_fonts = Array( + 'serif' => 'serif', + 'sans-serif' => 'sans-serif', + 'sans serif' => 'sans-serif', + 'sansserif' => 'sans-serif', + 'sans' => 'sans-serif', + 'cursive' => 'cursive', + 'fantasy' => 'fantasy', + 'monospace' => 'monospace', + 'mono' => 'monospace', + ); + foreach ($fonts as $font) { + $font = trim($font); + if (isset($special_fonts[$font])) { + if (strlen($result) > 0) $result .= ","; + $result .= $special_fonts[$font]; + } + else if (strlen($font) > 0) { + if (strlen($result) > 0) $result .= ","; + $result .= "'$font'"; + } + } + return "$content"; + } + + // Format a [wiki] tag by producing an ... element. + function DoWiki($bbcode, $action, $name, $default, $params, $content) { + $name = $bbcode->Wikify($default); + if ($action == BBCODE_CHECK) + return strlen($name) > 0; + $title = trim(@$params['title']); + if (strlen($title) <= 0) $title = trim($default); + return "wiki_url}$name\" class=\"bbcode_wiki\">" + . htmlspecialchars($title) . ""; + } + + // Format an [img] tag. The URL only allows http, https, and ftp protocols for safety. + function DoImage($bbcode, $action, $name, $default, $params, $content) { + // We can't validate this until we have its content. + if ($action == BBCODE_CHECK) return true; + + $content = trim($bbcode->UnHTMLEncode(strip_tags($content))); + if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { + if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { + // No protocol, so the image is in our local image directory, or somewhere under it. + if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { + $info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); + if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { + return "local_img_url}/{$content}") . "\" alt=\"" + . htmlspecialchars(basename($content)) . "\" width=\"" + . htmlspecialchars($info[0]) . "\" height=\"" + . htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; + } + } + } + else if ($bbcode->IsValidURL($content, false)) { + // Remote URL, or at least we don't know where it is. + return "\"""; + } + } + + return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); + } + + // Format a [rule] tag. This substitutes the content provided by the BBCode + // object, whatever that may be. + function DoRule($bbcode, $action, $name, $default, $params, $content) { + if ($action == BBCODE_CHECK) return true; + else return $bbcode->rule_html; + } + + // Format a [quote] tag. This tag can come in a variety of flavors: + // + // [quote]...[/quote] + // [quote=Tom]...[/quote] + // [quote name="Tom"]...[/quote] + // + // In the third form, you can also add a date="" parameter to display the date + // on which Tom wrote it, and you can add a url="" parameter to turn the author's + // name into a link. A full example might be: + // + // [quote name="Tom" date="July 4, 1776 3:48 PM" url="http://www.constitution.gov"]...[/quote] + // + // The URL only allows http, https, mailto, gopher, ftp, and feed protocols for safety. + function DoQuote($bbcode, $action, $name, $default, $params, $content) { + if ($action == BBCODE_CHECK) return true; + + if (isset($params['name'])) { + $title = htmlspecialchars(trim($params['name'])) . " wrote"; + if (isset($params['date'])) + $title .= " on " . htmlspecialchars(trim($params['date'])); + $title .= ":"; + if (isset($params['url'])) { + $url = trim($params['url']); + if ($bbcode->IsValidURL($url)) + $title = "" . $title . ""; + } + } + else if (!is_string($default)) + $title = "Quote:"; + else $title = htmlspecialchars(trim($default)) . " wrote:"; + return "\n
    \n
    " + . $title . "
    \n
    " + . $content . "
    \n
    \n"; + } + + // Format a [list] tag, which is complicated by the number of different + // ways a list can be started. The following parameters are allowed: + // + // [list] Unordered list, using default marker + // [list=circle] Unordered list, using circle marker + // [list=disc] Unordered list, using disc marker + // [list=square] Unordered list, using square marker + // + // [list=1] Ordered list, numeric, starting at 1 + // [list=A] Ordered list, capital letters, starting at A + // [list=a] Ordered list, lowercase letters, starting at a + // [list=I] Ordered list, capital Roman numerals, starting at I + // [list=i] Ordered list, lowercase Roman numerals, starting at i + // [list=greek] Ordered list, lowercase Greek letters, starting at alpha + // [list=01] Ordered list, two-digit numeric with 0-padding, starting at 01 + function DoList($bbcode, $action, $name, $default, $params, $content) { + + // Allowed list styles, striaght from the CSS 2.1 spec. The only prohibited + // list style is that with image-based markers, which often slows down web sites. + $list_styles = Array( + '1' => 'decimal', + '01' => 'decimal-leading-zero', + 'i' => 'lower-roman', + 'I' => 'upper-roman', + 'a' => 'lower-alpha', + 'A' => 'upper-alpha', + ); + $ci_list_styles = Array( + 'circle' => 'circle', + 'disc' => 'disc', + 'square' => 'square', + 'greek' => 'lower-greek', + 'armenian' => 'armenian', + 'georgian' => 'georgian', + ); + $ul_types = Array( + 'circle' => 'circle', + 'disc' => 'disc', + 'square' => 'square', + ); + + $default = trim($default); + + if ($action == BBCODE_CHECK) { + if (!is_string($default) || strlen($default) == "") return true; + else if (isset($list_styles[$default])) return true; + else if (isset($ci_list_styles[strtolower($default)])) return true; + else return false; + } + + // Choose a list element (', - 'ol' => '%s', - 'li' => '%s', - 'error' => '%s', - 'javascriptblock' => '', - 'javascriptstart' => '', - 'javascriptend' => '' - ); - -/** - * Minimized attributes - * - * @var array - */ - protected $_minimizedAttributes = array( - 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected', - 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize' - ); - -/** - * Format to attribute - * - * @var string - */ - protected $_attributeFormat = '%s="%s"'; - -/** - * Format to attribute - * - * @var string - */ - protected $_minimizedAttributeFormat = '%s="%s"'; - -/** - * Breadcrumbs. - * - * @var array - */ - protected $_crumbs = array(); - -/** - * Names of script files that have been included once - * - * @var array - */ - protected $_includedScripts = array(); -/** - * Options for the currently opened script block buffer if any. - * - * @var array - */ - protected $_scriptBlockOptions = array(); -/** - * Document type definitions - * - * @var array - */ - protected $_docTypes = array( - 'html4-strict' => '', - 'html4-trans' => '', - 'html4-frame' => '', - 'html5' => '', - 'xhtml-strict' => '', - 'xhtml-trans' => '', - 'xhtml-frame' => '', - 'xhtml11' => '' - ); - -/** - * Constructor - * - * ### Settings - * - * - `configFile` A file containing an array of tags you wish to redefine. - * - * ### Customizing tag sets - * - * Using the `configFile` option you can redefine the tag HtmlHelper will use. - * The file named should be compatible with HtmlHelper::loadConfig(). - * - * @param View $View The View this helper is being attached to. - * @param array $settings Configuration settings for the helper. - */ - public function __construct(View $View, $settings = array()) { - parent::__construct($View, $settings); - if (!empty($settings['configFile'])) { - $this->loadConfig($settings['configFile']); - } - } - -/** - * Adds a link to the breadcrumbs array. - * - * @param string $name Text for link - * @param string $link URL for link (if empty it won't be a link) - * @param mixed $options Link attributes e.g. array('id'=>'selected') - * @return void - * @see HtmlHelper::link() for details on $options that can be used. - */ - public function addCrumb($name, $link = null, $options = null) { - $this->_crumbs[] = array($name, $link, $options); - } - -/** - * Returns a doctype string. - * - * Possible doctypes: - * - * - html4-strict: HTML4 Strict. - * - html4-trans: HTML4 Transitional. - * - html4-frame: HTML4 Frameset. - * - html5: HTML5. - * - xhtml-strict: XHTML1 Strict. - * - xhtml-trans: XHTML1 Transitional. - * - xhtml-frame: XHTML1 Frameset. - * - xhtml11: XHTML1.1. - * - * @param string $type Doctype to use. - * @return string Doctype string - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::docType - */ - public function docType($type = 'xhtml-strict') { - if (isset($this->_docTypes[$type])) { - return $this->_docTypes[$type]; - } - return null; - } - -/** - * Creates a link to an external resource and handles basic meta tags - * - * ### Options - * - * - `inline` Whether or not the link element should be output inline, or in scripts_for_layout. - * - * @param string $type The title of the external resource - * @param mixed $url The address of the external resource or string for content attribute - * @param array $options Other attributes for the generated tag. If the type attribute is html, - * rss, atom, or icon, the mime-type is returned. - * @return string A completed `` element. - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::meta - */ - public function meta($type, $url = null, $options = array()) { - $inline = isset($options['inline']) ? $options['inline'] : true; - unset($options['inline']); - - if (!is_array($type)) { - $types = array( - 'rss' => array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url), - 'atom' => array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url), - 'icon' => array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url), - 'keywords' => array('name' => 'keywords', 'content' => $url), - 'description' => array('name' => 'description', 'content' => $url), - ); - - if ($type === 'icon' && $url === null) { - $types['icon']['link'] = $this->webroot('favicon.ico'); - } - - if (isset($types[$type])) { - $type = $types[$type]; - } elseif (!isset($options['type']) && $url !== null) { - if (is_array($url) && isset($url['ext'])) { - $type = $types[$url['ext']]; - } else { - $type = $types['rss']; - } - } elseif (isset($options['type']) && isset($types[$options['type']])) { - $type = $types[$options['type']]; - unset($options['type']); - } else { - $type = array(); - } - } elseif ($url !== null) { - $inline = $url; - } - $options = array_merge($type, $options); - $out = null; - - if (isset($options['link'])) { - if (isset($options['rel']) && $options['rel'] === 'icon') { - $out = sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' ')); - $options['rel'] = 'shortcut icon'; - } else { - $options['link'] = $this->url($options['link'], true); - } - $out .= sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' ')); - } else { - $out = sprintf($this->_tags['meta'], $this->_parseAttributes($options, array('type'), ' ', ' ')); - } - - if ($inline) { - return $out; - } else { - $this->_View->addScript($out); - } - } - -/** - * Returns a charset META-tag. - * - * @param string $charset The character set to be used in the meta tag. If empty, - * The App.encoding value will be used. Example: "utf-8". - * @return string A meta tag containing the specified character set. - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::charset - */ - public function charset($charset = null) { - if (empty($charset)) { - $charset = strtolower(Configure::read('App.encoding')); - } - return sprintf($this->_tags['charset'], (!empty($charset) ? $charset : 'utf-8')); - } - -/** - * Creates an HTML link. - * - * If $url starts with "http://" this is treated as an external link. Else, - * it is treated as a path to controller/action and parsed with the - * HtmlHelper::url() method. - * - * If the $url is empty, $title is used instead. - * - * ### Options - * - * - `escape` Set to false to disable escaping of title and attributes. - * - `confirm` JavaScript confirmation message. - * - * @param string $title The content to be wrapped by tags. - * @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://) - * @param array $options Array of HTML attributes. - * @param string $confirmMessage JavaScript confirmation message. - * @return string An `` element. - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::link - */ - public function link($title, $url = null, $options = array(), $confirmMessage = false) { - $escapeTitle = true; - if ($url !== null) { - $url = $this->url($url); - } else { - $url = $this->url($title); - $title = $url; - $escapeTitle = false; - } - - if (isset($options['escape'])) { - $escapeTitle = $options['escape']; - } - - if ($escapeTitle === true) { - $title = h($title); - } elseif (is_string($escapeTitle)) { - $title = htmlentities($title, ENT_QUOTES, $escapeTitle); - } - - if (!empty($options['confirm'])) { - $confirmMessage = $options['confirm']; - unset($options['confirm']); - } - if ($confirmMessage) { - $confirmMessage = str_replace("'", "\'", $confirmMessage); - $confirmMessage = str_replace('"', '\"', $confirmMessage); - $options['onclick'] = "return confirm('{$confirmMessage}');"; - } elseif (isset($options['default']) && $options['default'] == false) { - if (isset($options['onclick'])) { - $options['onclick'] .= ' event.returnValue = false; return false;'; - } else { - $options['onclick'] = 'event.returnValue = false; return false;'; - } - unset($options['default']); - } - return sprintf($this->_tags['link'], $url, $this->_parseAttributes($options), $title); - } - -/** - * Creates a link element for CSS stylesheets. - * - * ### Usage - * - * Include one CSS file: - * - * `echo $this->Html->css('styles.css');` - * - * Include multiple CSS files: - * - * `echo $this->Html->css(array('one.css', 'two.css'));` - * - * Add the stylesheet to the `$scripts_for_layout` layout var: - * - * `$this->Html->css('styles.css', null, array('inline' => false));` - * - * ### Options - * - * - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true - * - * @param mixed $path The name of a CSS style sheet or an array containing names of - * CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot - * of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css. - * @param string $rel Rel attribute. Defaults to "stylesheet". If equal to 'import' the stylesheet will be imported. - * @param array $options Array of HTML attributes. - * @return string CSS or - - -

    - - \ No newline at end of file diff --git a/app/Cake/View/Layouts/js/default.ctp b/app/Cake/View/Layouts/js/default.ctp deleted file mode 100644 index d94dc903..00000000 --- a/app/Cake/View/Layouts/js/default.ctp +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/app/Cake/View/Layouts/rss/default.ctp b/app/Cake/View/Layouts/rss/default.ctp deleted file mode 100644 index d57624e1..00000000 --- a/app/Cake/View/Layouts/rss/default.ctp +++ /dev/null @@ -1,14 +0,0 @@ -Rss->document( - $this->Rss->channel( - array(), $channel, $content_for_layout - ) -); -?> diff --git a/app/Cake/View/Layouts/xml/default.ctp b/app/Cake/View/Layouts/xml/default.ctp deleted file mode 100644 index 1025a55f..00000000 --- a/app/Cake/View/Layouts/xml/default.ctp +++ /dev/null @@ -1,2 +0,0 @@ -'; ?> - diff --git a/app/Cake/View/MediaView.php b/app/Cake/View/MediaView.php deleted file mode 100644 index eeb789f1..00000000 --- a/app/Cake/View/MediaView.php +++ /dev/null @@ -1,248 +0,0 @@ -viewClass = 'Media'; - * $params = array( - * 'id' => 'example.zip', - * 'name' => 'example', - * 'download' => true, - * 'extension' => 'zip', - * 'path' => APP . 'files' . DS - * ); - * $this->set($params); - * } - * } - * }}} - * - * @package Cake.View - */ -class MediaView extends View { -/** - * Indicates whether response gzip compression was enabled for this class - * - * @var boolean - */ - protected $_compressionEnabled = false; - -/** - * Reference to the Response object responsible for sending the headers - * - * @var CakeResponse - */ - public $response = null; - -/** - * Constructor - * - * @param Controller $controller The controller with viewVars - */ - public function __construct($controller = null) { - parent::__construct($controller); - if (is_object($controller) && isset($controller->response)) { - $this->response = $controller->response; - } else { - $this->response = new CakeResponse; - } - } - -/** - * Display or download the given file - * - * @param string $view Not used - * @param string $layout Not used - * @return mixed - * @throws NotFoundException - */ - public function render($view = null, $layout = null) { - $name = $download = $extension = $id = $modified = $path = $cache = $mimeType = $compress = null; - extract($this->viewVars, EXTR_OVERWRITE); - - if (is_dir($path)) { - $path = $path . $id; - } else { - $path = APP . $path . $id; - } - - if (!is_file($path)) { - if (Configure::read('debug')) { - throw new NotFoundException(sprintf('The requested file %s was not found', $path)); - } - throw new NotFoundException('The requested file was not found'); - } - - if (is_array($mimeType)) { - $this->response->type($mimeType); - } - - if (isset($extension) && $this->_isActive()) { - $extension = strtolower($extension); - $chunkSize = 8192; - $buffer = ''; - $fileSize = @filesize($path); - $handle = fopen($path, 'rb'); - - if ($handle === false) { - return false; - } - if (!empty($modified) && !is_numeric($modified)) { - $modified = strtotime($modified, time()); - } else { - $modified = time(); - } - if ($this->response->type($extension) === false) { - $download = true; - } - - if ($cache) { - $this->response->cache($modified, $cache); - } else { - $this->response->header(array( - 'Date' => gmdate('D, d M Y H:i:s', time()) . ' GMT', - 'Expires' => '0', - 'Cache-Control' => 'private, must-revalidate, post-check=0, pre-check=0', - 'Pragma' => 'no-cache' - )); - } - - if ($download) { - $agent = env('HTTP_USER_AGENT'); - - if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) { - $contentType = 'application/octetstream'; - } else if (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { - $contentType = 'application/force-download'; - } - - if (!empty($contentType)) { - $this->response->type($contentType); - } - if (is_null($name)) { - $name = $id; - } - $this->response->download($name); - $this->response->header(array('Accept-Ranges' => 'bytes')); - - $httpRange = env('HTTP_RANGE'); - if (isset($httpRange)) { - list($toss, $range) = explode('=', $httpRange); - - $size = $fileSize - 1; - $length = $fileSize - $range; - - $this->response->header(array( - 'Content-Length' => $length, - 'Content-Range' => 'bytes ' . $range . $size . '/' . $fileSize - )); - - $this->response->statusCode(206); - fseek($handle, $range); - } else { - $this->response->header('Content-Length', $fileSize); - } - } else { - $this->response->header(array( - 'Content-Length' => $fileSize - )); - } - $this->_clearBuffer(); - if ($compress) { - $this->_compressionEnabled = $this->response->compress(); - } - - $this->response->send(); - return $this->_sendFile($handle); - } - - return false; - } - -/** - * Reads out a file handle, and echos the content to the client. - * - * @param resource $handle A file handle or stream - * @return void - */ - protected function _sendFile($handle) { - $chunkSize = 8192; - $buffer = ''; - while (!feof($handle)) { - if (!$this->_isActive()) { - fclose($handle); - return false; - } - set_time_limit(0); - $buffer = fread($handle, $chunkSize); - echo $buffer; - if (!$this->_compressionEnabled) { - $this->_flushBuffer(); - } - } - fclose($handle); - } - -/** - * Returns true if connection is still active - * - * @return boolean - */ - protected function _isActive() { - return connection_status() == 0 && !connection_aborted(); - } - -/** - * Clears the contents of the topmost output buffer and discards them - * - * @return boolean - */ - protected function _clearBuffer() { - return @ob_end_clean(); - } - -/** - * Flushes the contents of the output buffer - * - * @return void - */ - protected function _flushBuffer() { - @flush(); - @ob_flush(); - } -} diff --git a/app/Cake/View/Pages/home.ctp b/app/Cake/View/Pages/home.ctp deleted file mode 100644 index 4a2ac0da..00000000 --- a/app/Cake/View/Pages/home.ctp +++ /dev/null @@ -1,186 +0,0 @@ - - -

    - - 0): - Debugger::checkSecurityKeys(); -endif; -?> -

    - - 1) Help me configure it - 2) I don't / can't use URL rewriting -

    -

    -=')): - echo ''; - echo __d('cake_dev', 'Your version of PHP is 5.2.6 or higher.'); - echo ''; - else: - echo ''; - echo __d('cake_dev', 'Your version of PHP is too low. You need PHP 5.2.6 or higher to use CakePHP.'); - echo ''; - endif; -?> -

    -

    - '; - echo __d('cake_dev', 'Your tmp directory is writable.'); - echo ''; - else: - echo ''; - echo __d('cake_dev', 'Your tmp directory is NOT writable.'); - echo ''; - endif; - ?> -

    -

    - '; - echo __d('cake_dev', 'The %s is being used for caching. To change the config edit APP/Config/core.php ', ''. $settings['engine'] . 'Engine'); - echo ''; - else: - echo ''; - echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in APP/Config/core.php'); - echo ''; - endif; - ?> -

    -

    - '; - echo __d('cake_dev', 'Your database configuration file is present.'); - $filePresent = true; - echo ''; - else: - echo ''; - echo __d('cake_dev', 'Your database configuration file is NOT present.'); - echo '
    '; - echo __d('cake_dev', 'Rename APP/Config/database.php.default to APP/Config/database.php'); - echo '
    '; - endif; - ?> -

    - -

    - isConnected()): - echo ''; - echo __d('cake_dev', 'Cake is able to connect to the database.'); - echo ''; - else: - echo ''; - echo __d('cake_dev', 'Cake is NOT able to connect to the database.'); - echo ''; - endif; - ?> -

    - -'; - __d('cake_dev', 'PCRE has not been compiled with Unicode support.'); - echo '
    '; - __d('cake_dev', 'Recompile PCRE with Unicode support by adding --enable-unicode-properties when configuring'); - echo '

    '; - } -?> -

    -

    - -To change its layout, create: APP/View/Layouts/default.ctp.
    -You can also add some CSS styles for your pages at: APP/webroot/css.'); -?> -

    - -

    -

    - Html->link( - sprintf('%s %s', __d('cake_dev', 'New'), __d('cake_dev', 'CakePHP 1.3 Docs')), - 'http://book.cakephp.org/2.0/en/', - array('target' => '_blank', 'escape' => false) - ); - ?> -

    -

    - Html->link( - __d('cake_dev', 'The 15 min Blog Tutorial'), - 'http://book.cakephp.org/2.0/en/tutorials-and-examples/blog/blog.html', - array('target' => '_blank', 'escape' => false) - ); - ?> -

    - -

    -

    - -

    -

    - -

    - - diff --git a/app/Cake/View/ScaffoldView.php b/app/Cake/View/ScaffoldView.php deleted file mode 100644 index 62c46f04..00000000 --- a/app/Cake/View/ScaffoldView.php +++ /dev/null @@ -1,89 +0,0 @@ -action; - } - $name = Inflector::underscore($name); - $prefixes = Configure::read('Routing.prefixes'); - - if (!empty($prefixes)) { - foreach ($prefixes as $prefix) { - if (strpos($name, $prefix . '_') !== false) { - $name = substr($name, strlen($prefix) + 1); - break; - } - } - } - - if ($name === 'add' || $name == 'edit') { - $name = 'form'; - } - - $scaffoldAction = 'scaffold.' . $name; - - if (!is_null($this->subDir)) { - $subDir = strtolower($this->subDir) . DS; - } else { - $subDir = null; - } - - $names[] = $this->viewPath . DS . $subDir . $scaffoldAction; - $names[] = 'Scaffolds' . DS . $subDir . $name; - - $paths = $this->_paths($this->plugin); - $exts = array($this->ext); - if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); - } - foreach ($exts as $ext) { - foreach ($paths as $path) { - foreach ($names as $name) { - if (file_exists($path . $name . $ext)) { - return $path . $name . $ext; - } - } - } - } - - if ($name === 'Scaffolds' . DS . $subDir . 'error') { - return CAKE . 'View' . DS . 'Errors' . DS . 'scaffold_error.ctp'; - } - - throw new MissingViewException($paths[0] . $name . $this->ext); - } -} diff --git a/app/Cake/View/Scaffolds/form.ctp b/app/Cake/View/Scaffolds/form.ctp deleted file mode 100644 index 379ff6f1..00000000 --- a/app/Cake/View/Scaffolds/form.ctp +++ /dev/null @@ -1,51 +0,0 @@ - -
    -Form->create(); - echo $this->Form->inputs($scaffoldFields, array('created', 'modified', 'updated')); - echo $this->Form->end(__d('cake', 'Submit')); -?> -
    -
    -

    -
      -request->action != 'add'): ?> -
    • Form->postLink( - __d('cake', 'Delete'), - array('action' => 'delete', $this->Form->value($modelClass . '.' . $primaryKey)), - null, - __d('cake', 'Are you sure you want to delete # %s?', $this->Form->value($modelClass . '.' . $primaryKey))); - ?>
    • - -
    • Html->link(__d('cake', 'List') . ' ' . $pluralHumanName, array('action' => 'index'));?>
    • - $_data) { - foreach ($_data as $_alias => $_details) { - if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "\t\t
    • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' =>'index')) . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' =>'add')) . "
    • \n"; - $done[] = $_details['controller']; - } - } - } -?> -
    -
    \ No newline at end of file diff --git a/app/Cake/View/Scaffolds/index.ctp b/app/Cake/View/Scaffolds/index.ctp deleted file mode 100644 index 0b543400..00000000 --- a/app/Cake/View/Scaffolds/index.ctp +++ /dev/null @@ -1,94 +0,0 @@ - -
    -

    - - - - - - - -"; - foreach ($scaffoldFields as $_field) { - $isKey = false; - if (!empty($associations['belongsTo'])) { - foreach ($associations['belongsTo'] as $_alias => $_details) { - if ($_field === $_details['foreignKey']) { - $isKey = true; - echo ""; - break; - } - } - } - if ($isKey !== true) { - echo ""; - } - } - - echo ''; - echo ''; - -endforeach; - -?> -
    Paginator->sort($_field);?>
    " . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "" . h(${$singularVar}[$modelClass][$_field]) . "'; - echo $this->Html->link(__d('cake', 'View'), array('action' => 'view', ${$singularVar}[$modelClass][$primaryKey])); - echo $this->Html->link(__d('cake', 'Edit'), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])); - echo $this->Form->postLink( - __d('cake', 'Delete'), - array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), - null, - __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] - ); - echo '
    -

    Paginator->counter(array( - 'format' => __d('cake', 'Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') - )); - ?>

    -
    - Paginator->prev('< ' . __d('cake', 'previous'), array(), null, array('class' => 'prev disabled')); - echo $this->Paginator->numbers(array('separator' => '')); - echo $this->Paginator->next(__d('cake', 'next') .' >', array(), null, array('class' => 'next disabled')); - ?> -
    -
    -
    -

    -
      -
    • Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')); ?>
    • - $_data) { - foreach ($_data as $_alias => $_details) { - if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "
    • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
    • "; - echo "
    • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
    • "; - $done[] = $_details['controller']; - } - } - } -?> -
    -
    diff --git a/app/Cake/View/Scaffolds/view.ctp b/app/Cake/View/Scaffolds/view.ctp deleted file mode 100644 index 9e36fffd..00000000 --- a/app/Cake/View/Scaffolds/view.ctp +++ /dev/null @@ -1,146 +0,0 @@ - -
    -

    -
    - $_details) { - if ($_field === $_details['foreignKey']) { - $isKey = true; - echo "\t\t
    " . Inflector::humanize($_alias) . "
    \n"; - echo "\t\t
    \n\t\t\t" . $this->Html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t 
    \n"; - break; - } - } - } - if ($isKey !== true) { - echo "\t\t
    " . Inflector::humanize($_field) . "
    \n"; - echo "\t\t
    " . h(${$singularVar}[$modelClass][$_field]) . " 
    \n"; - } -} -?> -
    -
    -
    -

    -
      -" .$this->Html->link(__d('cake', 'Edit %s', $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])). " \n"; - echo "\t\t
    • " .$this->Html->link(__d('cake', 'Delete %s', $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __d('cake', 'Are you sure you want to delete').' #' . ${$singularVar}[$modelClass][$primaryKey] . '?'). "
    • \n"; - echo "\t\t
    • " .$this->Html->link(__d('cake', 'List %s', $pluralHumanName), array('action' => 'index')). "
    • \n"; - echo "\t\t
    • " .$this->Html->link(__d('cake', 'New %s', $singularHumanName), array('action' => 'add')). "
    • \n"; - - $done = array(); - foreach ($associations as $_type => $_data) { - foreach ($_data as $_alias => $_details) { - if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { - echo "\t\t
    • " . $this->Html->link(__d('cake', 'List %s', Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "
    • \n"; - echo "\t\t
    • " . $this->Html->link(__d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "
    • \n"; - $done[] = $_details['controller']; - } - } - } -?> -
    -
    - $_details): ?> - - $_details): -$otherSingularVar = Inflector::variable($_alias); -?> - - diff --git a/app/Cake/View/ThemeView.php b/app/Cake/View/ThemeView.php deleted file mode 100644 index 0240f841..00000000 --- a/app/Cake/View/ThemeView.php +++ /dev/null @@ -1,73 +0,0 @@ -theme` and `$this->viewClass = 'Theme'` - * in your Controller to use the ThemeView. - * - * Example of theme path with `$this->theme = 'super_hot';` Would be `app/View/Themed/SuperHot/Posts` - * - * @package Cake.View - */ -class ThemeView extends View { -/** - * Constructor for ThemeView sets $this->theme. - * - * @param Controller $controller Controller object to be rendered. - */ - public function __construct($controller) { - parent::__construct($controller); - if ($controller) { - $this->theme = $controller->theme; - } - } - -/** - * Return all possible paths to find view files in order - * - * @param string $plugin The name of the plugin views are being found for. - * @param boolean $cached Set to true to force dir scan. - * @return array paths - * @todo Make theme path building respect $cached parameter. - */ - protected function _paths($plugin = null, $cached = true) { - $paths = parent::_paths($plugin, $cached); - $themePaths = array(); - - if (!empty($this->theme)) { - $count = count($paths); - for ($i = 0; $i < $count; $i++) { - if (strpos($paths[$i], DS . 'Plugin' . DS) === false - && strpos($paths[$i], DS . 'Cake' . DS . 'View') === false) { - if ($plugin) { - $themePaths[] = $paths[$i] . 'Themed'. DS . $this->theme . DS . 'Plugin' . DS . $plugin . DS; - } - $themePaths[] = $paths[$i] . 'Themed'. DS . $this->theme . DS; - } - } - $paths = array_merge($themePaths, $paths); - } - return $paths; - } -} diff --git a/app/Cake/View/View.php b/app/Cake/View/View.php deleted file mode 100644 index c982f716..00000000 --- a/app/Cake/View/View.php +++ /dev/null @@ -1,765 +0,0 @@ -set()` - * - * @package Cake.View - * @property CacheHelper $Cache - * @property FormHelper $Form - * @property HtmlHelper $Html - * @property JsHelper $Js - * @property NumberHelper $Number - * @property PaginatorHelper $Paginator - * @property RssHelper $Rss - * @property SessionHelper $Session - * @property TextHelper $Text - * @property TimeHelper $Time - */ -class View extends Object { - -/** - * Helpers collection - * - * @var HelperCollection - */ - public $Helpers; - -/** - * Name of the plugin. - * - * @link http://manual.cakephp.org/chapter/plugins - * @var string - */ - public $plugin = null; - -/** - * Name of the controller. - * - * @var string Name of controller - */ - public $name = null; - -/** - * Current passed params - * - * @var mixed - */ - public $passedArgs = array(); - -/** - * An array of names of built-in helpers to include. - * - * @var mixed A single name as a string or a list of names as an array. - */ - public $helpers = array('Html'); - -/** - * Path to View. - * - * @var string Path to View - */ - public $viewPath = null; - -/** - * Variables for the view - * - * @var array - */ - public $viewVars = array(); - -/** - * Name of view to use with this View. - * - * @var string - */ - public $view = null; - -/** - * Name of layout to use with this View. - * - * @var string - */ - public $layout = 'default'; - -/** - * Path to Layout. - * - * @var string Path to Layout - */ - public $layoutPath = null; - -/** - * Turns on or off Cake's conventional mode of applying layout files. On by default. - * Setting to off means that layouts will not be automatically applied to rendered views. - * - * @var boolean - */ - public $autoLayout = true; - -/** - * File extension. Defaults to Cake's template ".ctp". - * - * @var string - */ - public $ext = '.ctp'; - -/** - * Sub-directory for this view file. This is often used for extension based routing. - * for example with an `xml` extension, $subDir would be `xml/` - * - * @var string - */ - public $subDir = null; - -/** - * Theme name. If you are using themes, you should remember to use ThemeView as well. - * - * @var string - */ - public $theme = null; - -/** - * Used to define methods a controller that will be cached. - * - * @see Controller::$cacheAction - * @var mixed - */ - public $cacheAction = false; - -/** - * holds current errors for the model validation - * - * @var array - */ - public $validationErrors = array(); - -/** - * True when the view has been rendered. - * - * @var boolean - */ - public $hasRendered = false; - -/** - * List of generated DOM UUIDs - * - * @var array - */ - public $uuids = array(); - -/** - * Holds View output. - * - * @var string - */ - public $output = false; - -/** - * An instance of a CakeRequest object that contains information about the current request. - * This object contains all the information about a request and several methods for reading - * additional information about the request. - * - * @var CakeRequest - */ - public $request; - -/** - * The Cache configuration View will use to store cached elements. Changing this will change - * the default configuration elements are stored under. You can also choose a cache config - * per element. - * - * @var string - * @see View::element() - */ - public $elementCache = 'default'; - -/** - * List of variables to collect from the associated controller - * - * @var array - */ - protected $_passedVars = array( - 'viewVars', 'autoLayout', 'ext', 'helpers', 'view', 'layout', 'name', - 'layoutPath', 'viewPath', 'request', 'plugin', 'passedArgs', 'cacheAction' - ); - -/** - * Scripts (and/or other tags) for the layout - * - * @var array - */ - protected $_scripts = array(); - -/** - * Holds an array of paths. - * - * @var array - */ - protected $_paths = array(); - -/** - * boolean to indicate that helpers have been loaded. - * - * @var boolean - */ - protected $_helpersLoaded = false; - -/** - * Constructor - * - * @param Controller $controller A controller object to pull View::__passedArgs from. - */ - public function __construct($controller) { - if (is_object($controller)) { - $count = count($this->_passedVars); - for ($j = 0; $j < $count; $j++) { - $var = $this->_passedVars[$j]; - $this->{$var} = $controller->{$var}; - } - } - $this->Helpers = new HelperCollection($this); - parent::__construct(); - } - -/** - * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string. - * - * This realizes the concept of Elements, (or "partial layouts") and the $params array is used to send - * data to be used in the element. Elements can be cached improving performance by using the `cache` option. - * - * @param string $name Name of template file in the/app/View/Elements/ folder - * @param array $data Array of data to be made available to the rendered view (i.e. the Element) - * @param array $options Array of options. Possible keys are: - * - `cache` - Can either be `true`, to enable caching using the config in View::$elementCache. Or an array - * If an array, the following keys can be used: - * - `config` - Used to store the cached element in a custom cache configuration. - * - `key` - Used to define the key used in the Cache::write(). It will be prefixed with `element_` - * - `plugin` - Load an element from a specific plugin. - * - `callbacks` - Set to true to fire beforeRender and afterRender helper callbacks for this element. - * Defaults to false. - * @return string Rendered Element - */ - public function element($name, $data = array(), $options = array()) { - $file = $plugin = $key = null; - $callbacks = false; - - if (isset($options['plugin'])) { - $plugin = Inflector::camelize($options['plugin']); - } - if (isset($this->plugin) && !$plugin) { - $plugin = $this->plugin; - } - if (isset($options['callbacks'])) { - $callbacks = $options['callbacks']; - } - - if (isset($options['cache'])) { - $underscored = null; - if ($plugin) { - $underscored = Inflector::underscore($plugin); - } - $keys = array_merge(array($underscored, $name), array_keys($options), array_keys($data)); - $caching = array( - 'config' => $this->elementCache, - 'key' => implode('_', $keys) - ); - if (is_array($options['cache'])) { - $defaults = array( - 'config' => $this->elementCache, - 'key' => $caching['key'] - ); - $caching = array_merge($defaults, $options['cache']); - } - $key = 'element_' . $caching['key']; - $contents = Cache::read($key, $caching['config']); - if ($contents !== false) { - return $contents; - } - } - - $file = $this->_getElementFilename($name, $plugin); - - if ($file) { - if (!$this->_helpersLoaded) { - $this->loadHelpers(); - } - if ($callbacks) { - $this->Helpers->trigger('beforeRender', array($file)); - } - $element = $this->_render($file, array_merge($this->viewVars, $data)); - if ($callbacks) { - $this->Helpers->trigger('afterRender', array($file, $element)); - } - if (isset($options['cache'])) { - Cache::write($key, $element, $caching['config']); - } - return $element; - } - $file = 'Elements' . DS . $name . $this->ext; - - if (Configure::read('debug') > 0) { - return "Element Not Found: " . $file; - } - } - -/** - * Renders view for given view file and layout. - * - * Render triggers helper callbacks, which are fired before and after the view are rendered, - * as well as before and after the layout. The helper callbacks are called - * - * - `beforeRender` - * - `afterRender` - * - `beforeLayout` - * - `afterLayout` - * - * If View::$autoRender is false and no `$layout` is provided, the view will be returned bare. - * - * @param string $view Name of view file to use - * @param string $layout Layout to use. - * @return string Rendered Element - * @throws CakeException if there is an error in the view. - */ - public function render($view = null, $layout = null) { - if ($this->hasRendered) { - return true; - } - if (!$this->_helpersLoaded) { - $this->loadHelpers(); - } - $this->output = null; - - if ($view !== false && $viewFileName = $this->_getViewFileName($view)) { - $this->Helpers->trigger('beforeRender', array($viewFileName)); - $this->output = $this->_render($viewFileName); - $this->Helpers->trigger('afterRender', array($viewFileName)); - } - - if ($layout === null) { - $layout = $this->layout; - } - if ($this->output === false) { - throw new CakeException(__d('cake_dev', "Error in view %s, got no content.", $viewFileName)); - } - if ($layout && $this->autoLayout) { - $this->output = $this->renderLayout($this->output, $layout); - } - $this->hasRendered = true; - return $this->output; - } - -/** - * Renders a layout. Returns output from _render(). Returns false on error. - * Several variables are created for use in layout. - * - * - `title_for_layout` - A backwards compatible place holder, you should set this value if you want more control. - * - `content_for_layout` - contains rendered view file - * - `scripts_for_layout` - contains scripts added to header - * - * @param string $content_for_layout Content to render in a view, wrapped by the surrounding layout. - * @param string $layout Layout name - * @return mixed Rendered output, or false on error - * @throws CakeException if there is an error in the view. - */ - public function renderLayout($content_for_layout, $layout = null) { - $layoutFileName = $this->_getLayoutFileName($layout); - if (empty($layoutFileName)) { - return $this->output; - } - if (!$this->_helpersLoaded) { - $this->loadHelpers(); - } - $this->Helpers->trigger('beforeLayout', array($layoutFileName)); - - $this->viewVars = array_merge($this->viewVars, array( - 'content_for_layout' => $content_for_layout, - 'scripts_for_layout' => implode("\n\t", $this->_scripts), - )); - - if (!isset($this->viewVars['title_for_layout'])) { - $this->viewVars['title_for_layout'] = Inflector::humanize($this->viewPath); - } - - $this->output = $this->_render($layoutFileName); - - if ($this->output === false) { - throw new CakeException(__d('cake_dev', "Error in layout %s, got no content.", $layoutFileName)); - } - - $this->Helpers->trigger('afterLayout', array($layoutFileName)); - return $this->output; - } - -/** - * Render cached view. Works in concert with CacheHelper and Dispatcher to - * render cached view files. - * - * @param string $filename the cache file to include - * @param string $timeStart the page render start time - * @return boolean Success of rendering the cached file. - */ - public function renderCache($filename, $timeStart) { - ob_start(); - include ($filename); - - if (Configure::read('debug') > 0 && $this->layout != 'xml') { - echo ""; - } - $out = ob_get_clean(); - - if (preg_match('/^/', $out, $match)) { - if (time() >= $match['1']) { - @unlink($filename); - unset ($out); - return false; - } else { - if ($this->layout === 'xml') { - header('Content-type: text/xml'); - } - $commentLength = strlen(''); - echo substr($out, $commentLength); - return true; - } - } - } - -/** - * Returns a list of variables available in the current View context - * - * @return array Array of the set view variable names. - */ - public function getVars() { - return array_keys($this->viewVars); - } - -/** - * Returns the contents of the given View variable(s) - * - * @param string $var The view var you want the contents of. - * @return mixed The content of the named var if its set, otherwise null. - */ - public function getVar($var) { - if (!isset($this->viewVars[$var])) { - return null; - } else { - return $this->viewVars[$var]; - } - } - -/** - * Adds a script block or other element to be inserted in $scripts_for_layout in - * the `` of a document layout - * - * @param string $name Either the key name for the script, or the script content. Name can be used to - * update/replace a script element. - * @param string $content The content of the script being added, optional. - * @return void - */ - public function addScript($name, $content = null) { - if (empty($content)) { - if (!in_array($name, array_values($this->_scripts))) { - $this->_scripts[] = $name; - } - } else { - $this->_scripts[$name] = $content; - } - } - -/** - * Generates a unique, non-random DOM ID for an object, based on the object type and the target URL. - * - * @param string $object Type of object, i.e. 'form' or 'link' - * @param string $url The object's target URL - * @return string - */ - public function uuid($object, $url) { - $c = 1; - $url = Router::url($url); - $hash = $object . substr(md5($object . $url), 0, 10); - while (in_array($hash, $this->uuids)) { - $hash = $object . substr(md5($object . $url . $c), 0, 10); - $c++; - } - $this->uuids[] = $hash; - return $hash; - } - -/** - * Allows a template or element to set a variable that will be available in - * a layout or other element. Analagous to Controller::set(). - * - * @param mixed $one A string or an array of data. - * @param mixed $two Value in case $one is a string (which then works as the key). - * Unused if $one is an associative array, otherwise serves as the values to $one's keys. - * @return void - */ - public function set($one, $two = null) { - $data = null; - if (is_array($one)) { - if (is_array($two)) { - $data = array_combine($one, $two); - } else { - $data = $one; - } - } else { - $data = array($one => $two); - } - if ($data == null) { - return false; - } - $this->viewVars = $data + $this->viewVars; - } - -/** - * Magic accessor for helpers. Provides access to attributes that were deprecated. - * - * @param string $name Name of the attribute to get. - * @return mixed - */ - public function __get($name) { - if (isset($this->Helpers->{$name})) { - return $this->Helpers->{$name}; - } - switch ($name) { - case 'base': - case 'here': - case 'webroot': - case 'data': - return $this->request->{$name}; - case 'action': - return isset($this->request->params['action']) ? $this->request->params['action'] : ''; - case 'params': - return $this->request; - } - return null; - } - -/** - * Interact with the HelperCollection to load all the helpers. - * - * @return void - */ - public function loadHelpers() { - $helpers = HelperCollection::normalizeObjectArray($this->helpers); - foreach ($helpers as $name => $properties) { - list($plugin, $class) = pluginSplit($properties['class']); - $this->{$class} = $this->Helpers->load($properties['class'], $properties['settings']); - } - $this->_helpersLoaded = true; - } - -/** - * Renders and returns output for given view filename with its - * array of data. - * - * @param string $___viewFn Filename of the view - * @param array $___dataForView Data to include in rendered view. If empty the current View::$viewVars will be used. - * @return string Rendered output - */ - protected function _render($___viewFn, $___dataForView = array()) { - if (empty($___dataForView)) { - $___dataForView = $this->viewVars; - } - - extract($___dataForView, EXTR_SKIP); - ob_start(); - - include $___viewFn; - - return ob_get_clean(); - } - -/** - * Loads a helper. Delegates to the `HelperCollection::load()` to load the helper - * - * @param string $helperName Name of the helper to load. - * @param array $settings Settings for the helper - * @return Helper a constructed helper object. - * @see HelperCollection::load() - */ - public function loadHelper($helperName, $settings = array()) { - return $this->Helpers->load($helperName, $settings); - } - -/** - * Returns filename of given action's template file (.ctp) as a string. - * CamelCased action names will be under_scored! This means that you can have - * LongActionNames that refer to long_action_names.ctp views. - * - * @param string $name Controller action to find template filename for - * @return string Template filename - * @throws MissingViewException when a view file could not be found. - */ - protected function _getViewFileName($name = null) { - $subDir = null; - - if (!is_null($this->subDir)) { - $subDir = $this->subDir . DS; - } - - if ($name === null) { - $name = $this->view; - } - $name = str_replace('/', DS, $name); - - if (strpos($name, DS) === false && $name[0] !== '.') { - $name = $this->viewPath . DS . $subDir . Inflector::underscore($name); - } elseif (strpos($name, DS) !== false) { - if ($name[0] === DS || $name[1] === ':') { - if (is_file($name)) { - return $name; - } - $name = trim($name, DS); - } else if ($name[0] === '.') { - $name = substr($name, 3); - } else { - $name = $this->viewPath . DS . $subDir . $name; - } - } - $paths = $this->_paths($this->plugin); - - $exts = $this->_getExtensions(); - foreach ($exts as $ext) { - foreach ($paths as $path) { - if (file_exists($path . $name . $ext)) { - return $path . $name . $ext; - } - } - } - $defaultPath = $paths[0]; - - if ($this->plugin) { - $pluginPaths = App::path('plugins'); - foreach ($paths as $path) { - if (strpos($path, $pluginPaths[0]) === 0) { - $defaultPath = $path; - break; - } - } - } - throw new MissingViewException(array('file' => $defaultPath . $name . $this->ext)); - } - -/** - * Returns layout filename for this template as a string. - * - * @param string $name The name of the layout to find. - * @return string Filename for layout file (.ctp). - * @throws MissingLayoutException when a layout cannot be located - */ - protected function _getLayoutFileName($name = null) { - if ($name === null) { - $name = $this->layout; - } - $subDir = null; - - if (!is_null($this->layoutPath)) { - $subDir = $this->layoutPath . DS; - } - $paths = $this->_paths($this->plugin); - $file = 'Layouts' . DS . $subDir . $name; - - $exts = $this->_getExtensions(); - foreach ($exts as $ext) { - foreach ($paths as $path) { - if (file_exists($path . $file . $ext)) { - return $path . $file . $ext; - } - } - } - throw new MissingLayoutException(array('file' => $paths[0] . $file . $this->ext)); - } - - -/** - * Get the extensions that view files can use. - * - * @return array Array of extensions view files use. - */ - protected function _getExtensions() { - $exts = array($this->ext); - if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); - } - return $exts; - } - -/** - * Finds an element filename, returns false on failure. - * - * @param string $name The name of the element to find. - * @param string $plugin The plugin name the element is in. - * @return mixed Either a string to the element filename or false when one can't be found. - */ - protected function _getElementFileName($name, $plugin = null) { - $paths = $this->_paths($plugin); - $exts = $this->_getExtensions(); - foreach ($exts as $ext) { - foreach ($paths as $path) { - if (file_exists($path . 'Elements' . DS . $name . $ext)) { - return $path . 'Elements' . DS . $name . $ext; - } - } - } - return false; - } - -/** - * Return all possible paths to find view files in order - * - * @param string $plugin Optional plugin name to scan for view files. - * @param boolean $cached Set to true to force a refresh of view paths. - * @return array paths - */ - protected function _paths($plugin = null, $cached = true) { - if ($plugin === null && $cached === true && !empty($this->_paths)) { - return $this->_paths; - } - $paths = array(); - $viewPaths = App::path('View'); - $corePaths = array_flip(App::core('View')); - if (!empty($plugin)) { - $count = count($viewPaths); - for ($i = 0; $i < $count; $i++) { - if (!isset($corePaths[$viewPaths[$i]])) { - $paths[] = $viewPaths[$i] . 'Plugin' . DS . $plugin . DS; - } - } - $paths = array_merge($paths, App::path('View', $plugin)); - } - - $this->_paths = array_unique(array_merge($paths, $viewPaths, array_keys($corePaths))); - return $this->_paths; - } -} diff --git a/app/Cake/basics.php b/app/Cake/basics.php deleted file mode 100644 index 92a32268..00000000 --- a/app/Cake/basics.php +++ /dev/null @@ -1,732 +0,0 @@ - 0) { - $file = ''; - $line = ''; - if ($showFrom) { - $calledFrom = debug_backtrace(); - $file = substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1); - $line = $calledFrom[0]['line']; - } - $html = << -%s (line %s) -
    -%s
    -
    - -HTML; - $text = << $val) { - $sa[$key] = $val[$sortby]; - } - - if ($order == 'asc') { - asort($sa, $type); - } else { - arsort($sa, $type); - } - - foreach ($sa as $key => $val) { - $out[] = $array[$key]; - } - return $out; - } -} - -/** - * Convenience method for htmlspecialchars. - * - * @param string $text Text to wrap through htmlspecialchars - * @param boolean $double Encode existing html entities - * @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8' - * @return string Wrapped text - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#h - */ -function h($text, $double = true, $charset = null) { - if (is_array($text)) { - $texts = array(); - foreach ($text as $k => $t) { - $texts[$k] = h($t, $double, $charset); - } - return $texts; - } - - static $defaultCharset = false; - if ($defaultCharset === false) { - $defaultCharset = Configure::read('App.encoding'); - if ($defaultCharset === null) { - $defaultCharset = 'UTF-8'; - } - } - if (is_string($double)) { - $charset = $double; - } - return htmlspecialchars($text, ENT_QUOTES, ($charset) ? $charset : $defaultCharset, $double); -} - -/** - * Splits a dot syntax plugin name into its plugin and classname. - * If $name does not have a dot, then index 0 will be null. - * - * Commonly used like `list($plugin, $name) = pluginSplit($name);` - * - * @param string $name The name you want to plugin split. - * @param boolean $dotAppend Set to true if you want the plugin to have a '.' appended to it. - * @param string $plugin Optional default plugin to use if no plugin is found. Defaults to null. - * @return array Array with 2 indexes. 0 => plugin name, 1 => classname - */ -function pluginSplit($name, $dotAppend = false, $plugin = null) { - if (strpos($name, '.') !== false) { - $parts = explode('.', $name, 2); - if ($dotAppend) { - $parts[0] .= '.'; - } - return $parts; - } - return array($plugin, $name); -} - -/** - * Print_r convenience function, which prints out
     tags around
    - * the output of given array. Similar to debug().
    - *
    - * @see	debug()
    - * @param array $var Variable to print out
    - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#pr
    - */
    -function pr($var) {
    -	if (Configure::read('debug') > 0) {
    -		echo '
    ';
    -		print_r($var);
    -		echo '
    '; - } -} - -/** - * Merge a group of arrays - * - * @param array First array - * @param array Second array - * @param array Third array - * @param array Etc... - * @return array All array parameters merged into one - * @link http://book.cakephp.org/2.0/en/development/debugging.html#am - */ -function am() { - $r = array(); - $args = func_get_args(); - foreach ($args as $a) { - if (!is_array($a)) { - $a = array($a); - } - $r = array_merge($r, $a); - } - return $r; -} - -/** - * Gets an environment variable from available sources, and provides emulation - * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on - * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom - * environment information. - * - * @param string $key Environment variable name. - * @return string Environment variable setting. - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#env - */ -function env($key) { - if ($key === 'HTTPS') { - if (isset($_SERVER['HTTPS'])) { - return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); - } - return (strpos(env('SCRIPT_URI'), 'https://') === 0); - } - - if ($key === 'SCRIPT_NAME') { - if (env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) { - $key = 'SCRIPT_URL'; - } - } - - $val = null; - if (isset($_SERVER[$key])) { - $val = $_SERVER[$key]; - } elseif (isset($_ENV[$key])) { - $val = $_ENV[$key]; - } elseif (getenv($key) !== false) { - $val = getenv($key); - } - - if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) { - $addr = env('HTTP_PC_REMOTE_ADDR'); - if ($addr !== null) { - $val = $addr; - } - } - - if ($val !== null) { - return $val; - } - - switch ($key) { - case 'SCRIPT_FILENAME': - if (defined('SERVER_IIS') && SERVER_IIS === true) { - return str_replace('\\\\', '\\', env('PATH_TRANSLATED')); - } - break; - case 'DOCUMENT_ROOT': - $name = env('SCRIPT_NAME'); - $filename = env('SCRIPT_FILENAME'); - $offset = 0; - if (!strpos($name, '.php')) { - $offset = 4; - } - return substr($filename, 0, strlen($filename) - (strlen($name) + $offset)); - break; - case 'PHP_SELF': - return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME')); - break; - case 'CGI_MODE': - return (PHP_SAPI === 'cgi'); - break; - case 'HTTP_BASE': - $host = env('HTTP_HOST'); - $parts = explode('.', $host); - $count = count($parts); - - if ($count === 1) { - return '.' . $host; - } elseif ($count === 2) { - return '.' . $host; - } elseif ($count === 3) { - $gTLD = array( - 'aero', - 'asia', - 'biz', - 'cat', - 'com', - 'coop', - 'edu', - 'gov', - 'info', - 'int', - 'jobs', - 'mil', - 'mobi', - 'museum', - 'name', - 'net', - 'org', - 'pro', - 'tel', - 'travel', - 'xxx' - ); - if (in_array($parts[1], $gTLD)) { - return '.' . $host; - } - } - array_shift($parts); - return '.' . implode('.', $parts); - break; - } - return null; -} - -/** - * Reads/writes temporary data to cache files or session. - * - * @param string $path File path within /tmp to save the file. - * @param mixed $data The data to save to the temporary file. - * @param mixed $expires A valid strtotime string when the data expires. - * @param string $target The target of the cached data; either 'cache' or 'public'. - * @return mixed The contents of the temporary file. - * @deprecated Please use Cache::write() instead - */ -function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { - if (Configure::read('Cache.disable')) { - return null; - } - $now = time(); - - if (!is_numeric($expires)) { - $expires = strtotime($expires, $now); - } - - switch (strtolower($target)) { - case 'cache': - $filename = CACHE . $path; - break; - case 'public': - $filename = WWW_ROOT . $path; - break; - case 'tmp': - $filename = TMP . $path; - break; - } - $timediff = $expires - $now; - $filetime = false; - - if (file_exists($filename)) { - $filetime = @filemtime($filename); - } - - if ($data === null) { - if (file_exists($filename) && $filetime !== false) { - if ($filetime + $timediff < $now) { - @unlink($filename); - } else { - $data = @file_get_contents($filename); - } - } - } elseif (is_writable(dirname($filename))) { - @file_put_contents($filename, $data); - } - return $data; -} - -/** - * Used to delete files in the cache directories, or clear contents of cache directories - * - * @param mixed $params As String name to be searched for deletion, if name is a directory all files in - * directory will be deleted. If array, names to be searched for deletion. If clearCache() without params, - * all files in app/tmp/cache/views will be deleted - * @param string $type Directory in tmp/cache defaults to view directory - * @param string $ext The file extension you are deleting - * @return true if files found and deleted false otherwise - */ -function clearCache($params = null, $type = 'views', $ext = '.php') { - if (is_string($params) || $params === null) { - $params = preg_replace('/\/\//', '/', $params); - $cache = CACHE . $type . DS . $params; - - if (is_file($cache . $ext)) { - @unlink($cache . $ext); - return true; - } elseif (is_dir($cache)) { - $files = glob($cache . '*'); - - if ($files === false) { - return false; - } - - foreach ($files as $file) { - if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { - @unlink($file); - } - } - return true; - } else { - $cache = array( - CACHE . $type . DS . '*' . $params . $ext, - CACHE . $type . DS . '*' . $params . '_*' . $ext - ); - $files = array(); - while ($search = array_shift($cache)) { - $results = glob($search); - if ($results !== false) { - $files = array_merge($files, $results); - } - } - if (empty($files)) { - return false; - } - foreach ($files as $file) { - if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { - @unlink($file); - } - } - return true; - } - } elseif (is_array($params)) { - foreach ($params as $file) { - clearCache($file, $type, $ext); - } - return true; - } - return false; -} - -/** - * Recursively strips slashes from all values in an array - * - * @param array $values Array of values to strip slashes - * @return mixed What is returned from calling stripslashes - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#stripslashes_deep - */ -function stripslashes_deep($values) { - if (is_array($values)) { - foreach ($values as $key => $value) { - $values[$key] = stripslashes_deep($value); - } - } else { - $values = stripslashes($values); - } - return $values; -} - -/** - * Returns a translated string if one is found; Otherwise, the submitted message. - * - * @param string $singular Text to translate - * @param mixed $args Array with arguments or multiple arguments in function - * @return mixed translated string - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__ - */ -function __($singular, $args = null) { - if (!$singular) { - return; - } - - App::uses('I18n', 'I18n'); - $translated = I18n::translate($singular); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 1); - } - return vsprintf($translated, $args); -} - -/** - * Returns correct plural form of message identified by $singular and $plural for count $count. - * Some languages have more than one form for plural messages dependent on the count. - * - * @param string $singular Singular text to translate - * @param string $plural Plural text - * @param integer $count Count - * @param mixed $args Array with arguments or multiple arguments in function - * @return mixed plural form of translated string - */ -function __n($singular, $plural, $count, $args = null) { - if (!$singular) { - return; - } - - App::uses('I18n', 'I18n'); - $translated = I18n::translate($singular, $plural, null, 6, $count); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 3); - } - return vsprintf($translated, $args); -} - -/** - * Allows you to override the current domain for a single message lookup. - * - * @param string $domain Domain - * @param string $msg String to translate - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string - */ -function __d($domain, $msg, $args = null) { - if (!$msg) { - return; - } - App::uses('I18n', 'I18n'); - $translated = I18n::translate($msg, null, $domain); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 2); - } - return vsprintf($translated, $args); -} - -/** - * Allows you to override the current domain for a single plural message lookup. - * Returns correct plural form of message identified by $singular and $plural for count $count - * from domain $domain. - * - * @param string $domain Domain - * @param string $singular Singular string to translate - * @param string $plural Plural - * @param integer $count Count - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string - */ -function __dn($domain, $singular, $plural, $count, $args = null) { - if (!$singular) { - return; - } - App::uses('I18n', 'I18n'); - $translated = I18n::translate($singular, $plural, $domain, 6, $count); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 4); - } - return vsprintf($translated, $args); -} - -/** - * Allows you to override the current domain for a single message lookup. - * It also allows you to specify a category. - * - * The category argument allows a specific category of the locale settings to be used for fetching a message. - * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. - * - * Note that the category must be specified with a numeric value, instead of the constant name. The values are: - * - * - LC_ALL 0 - * - LC_COLLATE 1 - * - LC_CTYPE 2 - * - LC_MONETARY 3 - * - LC_NUMERIC 4 - * - LC_TIME 5 - * - LC_MESSAGES 6 - * - * @param string $domain Domain - * @param string $msg Message to translate - * @param integer $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string - */ -function __dc($domain, $msg, $category, $args = null) { - if (!$msg) { - return; - } - App::uses('I18n', 'I18n'); - $translated = I18n::translate($msg, null, $domain, $category); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 3); - } - return vsprintf($translated, $args); -} - -/** - * Allows you to override the current domain for a single plural message lookup. - * It also allows you to specify a category. - * Returns correct plural form of message identified by $singular and $plural for count $count - * from domain $domain. - * - * The category argument allows a specific category of the locale settings to be used for fetching a message. - * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. - * - * Note that the category must be specified with a numeric value, instead of the constant name. The values are: - * - * - LC_ALL 0 - * - LC_COLLATE 1 - * - LC_CTYPE 2 - * - LC_MONETARY 3 - * - LC_NUMERIC 4 - * - LC_TIME 5 - * - LC_MESSAGES 6 - * - * @param string $domain Domain - * @param string $singular Singular string to translate - * @param string $plural Plural - * @param integer $count Count - * @param integer $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string - */ -function __dcn($domain, $singular, $plural, $count, $category, $args = null) { - if (!$singular) { - return; - } - App::uses('I18n', 'I18n'); - $translated = I18n::translate($singular, $plural, $domain, $category, $count); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 5); - } - return vsprintf($translated, $args); -} - -/** - * The category argument allows a specific category of the locale settings to be used for fetching a message. - * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. - * - * Note that the category must be specified with a numeric value, instead of the constant name. The values are: - * - * - LC_ALL 0 - * - LC_COLLATE 1 - * - LC_CTYPE 2 - * - LC_MONETARY 3 - * - LC_NUMERIC 4 - * - LC_TIME 5 - * - LC_MESSAGES 6 - * - * @param string $msg String to translate - * @param integer $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string - */ -function __c($msg, $category, $args = null) { - if (!$msg) { - return; - } - App::uses('I18n', 'I18n'); - $translated = I18n::translate($msg, null, null, $category); - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 2); - } - return vsprintf($translated, $args); -} - -/** - * Shortcut to Log::write. - * - * @param string $message Message to write to log - */ -function LogError($message) { - App::uses('CakeLog', 'Log'); - $bad = array("\n", "\r", "\t"); - $good = ' '; - CakeLog::write('error', str_replace($bad, $good, $message)); -} - -/** - * Searches include path for files. - * - * @param string $file File to look for - * @return Full path to file if exists, otherwise false - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#fileExistsInPath - */ -function fileExistsInPath($file) { - $paths = explode(PATH_SEPARATOR, ini_get('include_path')); - foreach ($paths as $path) { - $fullPath = $path . DS . $file; - - if (file_exists($fullPath)) { - return $fullPath; - } elseif (file_exists($file)) { - return $file; - } - } - return false; -} - -/** - * Convert forward slashes to underscores and removes first and last underscores in a string - * - * @param string String to convert - * @return string with underscore remove from start and end of string - * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#convertSlash - */ -function convertSlash($string) { - $string = trim($string, '/'); - $string = preg_replace('/\/\//', '/', $string); - $string = str_replace('/', '_', $string); - return $string; -} diff --git a/app/Cake/bootstrap.php b/app/Cake/bootstrap.php deleted file mode 100644 index 76be5ec5..00000000 --- a/app/Cake/bootstrap.php +++ /dev/null @@ -1,154 +0,0 @@ -Duis tellus nunc, egestas a interdum sed, congue vitae magna. Curabitur a tellus quis lacus blandit sagittis a sit amet elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas metus sed neque ultricies.\r\n[button size=small color=silver]Read more[/button]

    \r\n','Services-LEFT',NULL),(12,'

    Integer egestas ultricies urna vitae molestie. Donec nec facilisis nisi. Vivamus tempor feugiat velit gravida vehicula. Donec faucibus pellentesque ipsum id varius. Ut rutrum metus sed neque ultricies a dictum ante sagittis.\r\n[button size=small color=silver]Read more[/button]

    \r\n','Services-CENTER',NULL),(13,'

    Praesent et metus sit amet nisl luctus commodo ut a risus. Mauris vehicula, ligula quis consectetur feugiat, arcu nibh tempor nisi, at varius dolor dolor nec dolor. Donec auctor mi vitae neque. Praesent sollicitudin egestas felis vitae gravida.\r\n[button size=small color=silver]Read more[/button]\r\n

    \r\n','Services-RIGHT',NULL); -/*!40000 ALTER TABLE `qa_block_custom` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_block_regions` --- - -DROP TABLE IF EXISTS `qa_block_regions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_block_regions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `block_id` int(11) NOT NULL, - `theme` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `region` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `ordering` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`,`block_id`) -) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_block_regions` --- - -LOCK TABLES `qa_block_regions` WRITE; -/*!40000 ALTER TABLE `qa_block_regions` DISABLE KEYS */; -INSERT INTO `qa_block_regions` VALUES (8,10,'AdminDefault','dashboard_main',1),(9,10,'Default','dashboard_main',1),(13,4,'AdminDefault','management-menu',1),(14,4,'Default','management-menu',1),(18,3,'AdminDefault','footer',1),(48,3,'Default','footer',1),(131,6,'Default','main-menu',1),(133,26,'Default','slider',1),(140,9,'Default','language-switcher',1),(151,19,'Default','services-left',1),(153,20,'Default','services-center',1),(155,25,'Default','services-right',1),(157,15,'Default','search',1),(159,12,'Default','services-left',2),(161,11,'Default','services-center',2),(163,13,'Default','services-right',2),(165,14,'Default','slider',2),(166,15,'AdminDefault','dashboard_sidebar',1),(169,7,'AdminDefault','dashboard_sidebar',2),(172,5,'Default','user-menu',2); -/*!40000 ALTER TABLE `qa_block_regions` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_block_roles` --- - -DROP TABLE IF EXISTS `qa_block_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_block_roles` ( - `block_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, - `user_role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from users_roles.rid.', - PRIMARY KEY (`block_id`,`user_role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_block_roles` --- - -LOCK TABLES `qa_block_roles` WRITE; -/*!40000 ALTER TABLE `qa_block_roles` DISABLE KEYS */; -INSERT INTO `qa_block_roles` VALUES ('1',3),('5',2); -/*!40000 ALTER TABLE `qa_block_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_blocks` --- - -DROP TABLE IF EXISTS `qa_blocks`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_blocks` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key - Unique block ID.', - `module` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The module from which the block originates; for example, ’user’ for the Who’s Online block, and ’block’ for any custom blocks.', - `delta` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'Unique ID for block within a module. Or menu_id', - `clone_of` int(11) NOT NULL DEFAULT '0', - `themes_cache` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'store all themes that belongs to (see block_regions table)', - `ordering` int(11) NOT NULL DEFAULT '1', - `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT 'Block enabled status. (1 = enabled, 0 = disabled)', - `visibility` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Flag to indicate how to show blocks on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)', - `pages` text COLLATE utf8_unicode_ci COMMENT 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.', - `title` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)', - `locale` text COLLATE utf8_unicode_ci, - `settings` text COLLATE utf8_unicode_ci, - `params` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores block settings, such as region and visibility...'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_blocks` --- - -LOCK TABLES `qa_blocks` WRITE; -/*!40000 ALTER TABLE `qa_blocks` DISABLE KEYS */; -INSERT INTO `qa_blocks` VALUES (1,'user','login',0,'',1,0,0,'','User Login','a:0:{}','',NULL),(2,'menu','navigation',0,':Default:',2,1,0,'','',NULL,'',NULL),(3,'system','powered_by',0,':AdminDefault:Default:',1,1,0,'','Powered By','a:0:{}','',NULL),(4,'menu','management',0,':AdminDefault:',1,1,1,'/admin/*','','a:0:{}','',NULL),(5,'menu','user-menu',0,':Default:',4,1,0,'','User Menu','a:0:{}','',NULL),(6,'menu','main-menu',0,':Default:',1,1,0,'','','a:0:{}','',NULL),(7,'user','new',0,':AdminDefault:',5,1,0,'','New Users','a:0:{}','a:1:{s:10:\"show_limit\";s:1:\"5\";}',NULL),(9,'locale','language_switcher',0,':Default:',3,1,0,'','Language switcher','a:0:{}','a:2:{s:5:\"flags\";s:1:\"1\";s:4:\"name\";s:1:\"1\";}',NULL),(10,'system','recent_content',0,':AdminDefault:',1,1,0,'','Updates','a:0:{}','',NULL),(11,'block','5',0,':Default:',1,1,0,'','WHAT WE DO','a:0:{}','',NULL),(12,'block','6',0,':Default:',1,1,0,'','OUR MISSION','a:0:{}','',NULL),(13,'block','7',0,':Default:',1,1,0,'','WHO WE ARE','a:0:{}','',NULL),(14,'theme_default','slider',0,':Default:',1,1,1,'/','Slider','a:0:{}','a:1:{s:12:\"slider_order\";s:52:\"1_[language].jpg\r\n2_[language].jpg\r\n3_[language].jpg\";}',NULL),(15,'node','search',0,':AdminDefault:Default:',1,1,0,'','Search','a:0:{}','',NULL),(16,'taxonomy','vocabularies',0,NULL,1,1,0,NULL,'Vocabularies',NULL,NULL,NULL); -/*!40000 ALTER TABLE `qa_blocks` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_comments` --- - -DROP TABLE IF EXISTS `qa_comments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_comments` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key: Unique comment ID.', - `node_id` int(11) NOT NULL COMMENT 'The node.nid to which this comment is a reply.', - `user_id` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid who authored the comment. If set to 0, this comment was created by an anonymous user.', - `body` text COLLATE utf8_unicode_ci, - `subject` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `hostname` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The author’s host name. (IP)', - `homepage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was created, as a Unix timestamp.', - `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was last edited, as a Unix timestamp.', - `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT 'The published status of a comment. (0 = Not Published, 1 = Published)', - `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s name. Uses users.name if the user is logged in, otherwise uses the value typed into the comment form.', - `mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s e-mail address from the comment form, if user is anonymous, and the ’Anonymous users may/must leave their contact information’ setting is turned on.', - PRIMARY KEY (`id`,`node_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores comments and associated data.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_comments` --- - -LOCK TABLES `qa_comments` WRITE; -/*!40000 ALTER TABLE `qa_comments` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_comments` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_field_data` --- - -DROP TABLE IF EXISTS `qa_field_data`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_field_data` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `field_id` int(11) NOT NULL, - `foreignKey` varchar(20) COLLATE utf8_unicode_ci NOT NULL, - `belongsTo` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `data` longtext COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `field_id` (`field_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_field_data` --- - -LOCK TABLES `qa_field_data` WRITE; -/*!40000 ALTER TABLE `qa_field_data` DISABLE KEYS */; -INSERT INTO `qa_field_data` VALUES (1,1,'1','Node','

    Content Boxes

    \r\n

    \r\n [content_box type=success]Maecenas pellentesque cursus auctor.[/content_box]

    \r\n

    \r\n [content_box type=error]Nam sagittis nisl non turpis aliquam mollis. Suspendisse ac metus nisi, sed vulputate arcu.[/content_box]

    \r\n

    \r\n [content_box type=alert]Cras interdum leo quis arcu sagittis pulvinar. Curabitur suscipit vulputate erat eu rhoncus. Morbi facilisis mi in ligula ornare ultricies.[/content_box]

    \r\n

    \r\n [content_box type=bubble]Fusce interdum cursus turpis vitae gravida. Aenean aliquet venenatis posuere. Etiam gravida ullamcorper purus.[/content_box]

    \r\n
    \r\n

    \r\n Buttons

    \r\n

    \r\n Using buttons hookTags, you can easily create a variety of buttons. These buttons all stem from a single tag, but vary in color and size (each of which are adjustable using color=”" and size=”" parameters).
    \r\n Allowed parameters:

    \r\n
      \r\n
    1. \r\n size: big, small
    2. \r\n
    3. \r\n color:\r\n
        \r\n
      • \r\n small: black, blue, green, lightblue, orange, pink, purple, red, silver, teal
      • \r\n
      • \r\n big: blue, green, orange, purple, red, turquoise
      • \r\n
      \r\n
    4. \r\n
    5. \r\n link: url of your button
    6. \r\n
    7. \r\n target: open link en new window (_blank), open in same window (_self or unset parameter)
    8. \r\n
    \r\n

    \r\n  

    \r\n

    \r\n  

    \r\n

    \r\n Small Buttons

    \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n [button color=black]Button text[/button]\r\n [button color=blue]Button text[/button]
    \r\n [button color=green]Button text[/button]\r\n [button color=lightblue]Button text[/button]
    \r\n [button color=orange]Button text[/button]\r\n [button color=pink]Button text[/button]
    \r\n [button color=purple]Button text[/button]\r\n [button color=red]Button text[/button]
    \r\n [button color=silver]Button text[/button]\r\n [button color=teal]Button text[/button]
    \r\n

    \r\n  

    \r\n

    \r\n  

    \r\n

    \r\n Big Buttons

    \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n [button color=blue size=big]Button text[/button]\r\n [button color=green size=big]Button text[/button]
    \r\n [button color=orange size=big]Button text[/button]\r\n [button color=purple size=big]Button text[/button]
    \r\n [button color=red size=big]Button text[/button]\r\n [button color=turquoise size=big]Button text[/button]
    \r\n

    \r\n  

    \r\n'),(2,1,'2','Node','Nam in iaculis lectus? Sed egestas dui quis leo porttitor vitae bibendum ipsum ultrices. Mauris nisi nulla, volutpat vel vestibulum non, lobortis sed lectus. Integer quis volutpat.'); -/*!40000 ALTER TABLE `qa_field_data` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_fields` --- - -DROP TABLE IF EXISTS `qa_fields`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_fields` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a field', - `name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The name of this field. Must be unique', - `label` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Human name', - `belongsTo` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, - `field_module` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The module that implements the field object', - `description` text COLLATE utf8_unicode_ci, - `required` tinyint(1) NOT NULL DEFAULT '0', - `settings` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Rendering settings (View mode)', - `ordering` int(11) DEFAULT '1' COMMENT 'edit form ordering', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Fields instances'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_fields` --- - -LOCK TABLES `qa_fields` WRITE; -/*!40000 ALTER TABLE `qa_fields` DISABLE KEYS */; -INSERT INTO `qa_fields` VALUES (1,'body','Body','NodeType-page','field_text','',1,'a:7:{s:7:\"display\";a:4:{s:7:\"default\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:1;s:11:\"trim_length\";s:3:\"180\";}s:4:\"full\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"600\";}s:4:\"list\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}s:3:\"rss\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}}s:4:\"type\";s:8:\"textarea\";s:11:\"text_format\";s:4:\"full\";s:7:\"max_len\";s:0:\"\";s:15:\"validation_rule\";s:0:\"\";s:18:\"validation_message\";s:0:\"\";s:15:\"text_processing\";s:4:\"full\";}',1); -/*!40000 ALTER TABLE `qa_fields` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_i18n` --- - -DROP TABLE IF EXISTS `qa_i18n`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_i18n` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `locale` varchar(6) COLLATE utf8_unicode_ci NOT NULL, - `model` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `foreign_key` int(10) NOT NULL, - `field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `content` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `locale` (`locale`), - KEY `model` (`model`), - KEY `row_id` (`foreign_key`), - KEY `field` (`field`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_i18n` --- - -LOCK TABLES `qa_i18n` WRITE; -/*!40000 ALTER TABLE `qa_i18n` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_i18n` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_languages` --- - -DROP TABLE IF EXISTS `qa_languages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_languages` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Language code, e.g. ’eng’', - `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Language name in English.', - `native` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Native language name.', - `direction` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'ltr' COMMENT 'Direction of language (Left-to-Right , Right-to-Left ).', - `icon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `status` int(11) NOT NULL DEFAULT '0' COMMENT 'Enabled flag (1 = Enabled, 0 = Disabled).', - `ordering` int(11) NOT NULL DEFAULT '0' COMMENT 'Weight, used in lists of languages.', - PRIMARY KEY (`id`), - UNIQUE KEY `code` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all available languages in the system.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_languages` --- - -LOCK TABLES `qa_languages` WRITE; -/*!40000 ALTER TABLE `qa_languages` DISABLE KEYS */; -INSERT INTO `qa_languages` VALUES (1,'eng','English','English','ltr','us.gif',1,0),(2,'spa','Spanish','Español','ltr','es.gif',1,0); -/*!40000 ALTER TABLE `qa_languages` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_menu_links` --- - -DROP TABLE IF EXISTS `qa_menu_links`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_menu_links` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `menu_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The menu name. All links with the same menu name (such as ’navigation’) are part of the same menu.', - `lft` int(11) NOT NULL, - `rght` int(11) NOT NULL, - `parent_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', - `link_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'external path', - `router_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'internal path', - `description` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `link_title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The text displayed for the link, which may be modified by a title callback stored in menu_router.', - `options` text COLLATE utf8_unicode_ci COMMENT 'A serialized array of HTML attributes options.', - `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'system' COMMENT 'The name of the module that generated this link.', - `target` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT '_self', - `expanded` tinyint(6) NOT NULL DEFAULT '0' COMMENT 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)', - `selected_on` text COLLATE utf8_unicode_ci COMMENT 'php code, or regular expression. based on selected_on_type', - `selected_on_type` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'php = on php return TRUE; reg = on URL match', - `status` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `router_path` (`router_path`(128)), - KEY `menu_id` (`menu_id`) -) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Contains the individual links within a menu.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_menu_links` --- - -LOCK TABLES `qa_menu_links` WRITE; -/*!40000 ALTER TABLE `qa_menu_links` DISABLE KEYS */; -INSERT INTO `qa_menu_links` VALUES (1,'management',1,2,0,NULL,'/admin/system/dashboard',NULL,'Dashboard',NULL,'system','_self',0,NULL,NULL,1),(2,'management',3,12,0,NULL,'/admin/system/structure',NULL,'Structure',NULL,'system','_self',0,NULL,NULL,1),(3,'management',13,14,0,NULL,'/admin/node/contents',NULL,'Content',NULL,'system','_self',0,NULL,NULL,1),(4,'management',15,16,0,NULL,'/admin/system/themes',NULL,'Appearance',NULL,'system','_self',0,NULL,NULL,1),(5,'management',17,18,0,NULL,'/admin/system/modules',NULL,'Modules',NULL,'system','_self',0,NULL,NULL,1),(6,'management',19,20,0,NULL,'/admin/user',NULL,'Users',NULL,'system','_self',0,NULL,NULL,1),(7,'management',23,24,0,NULL,'/admin/system/configuration',NULL,'Configuration',NULL,'system','_self',0,NULL,NULL,1),(8,'management',25,26,0,NULL,'/admin/system/help',NULL,'Help',NULL,'system','_self',0,NULL,NULL,1),(9,'management',4,5,2,NULL,'/admin/block','Configure what block content appears in your site\'s sidebars and other regions.','Blocks',NULL,'system','_self',0,NULL,NULL,1),(10,'management',6,7,2,NULL,'/admin/node/types','Manage content types.','Content Types',NULL,'system','_self',0,NULL,NULL,1),(11,'management',8,9,2,NULL,'/admin/menu','Add new menus to your site, edit existing menus, and rename and reorganize menu links.','Menus',NULL,'system','_self',0,NULL,NULL,1),(12,'management',10,11,2,NULL,'/admin/taxonomy','Manage tagging, categorization, and classification of your content.','Taxonomy',NULL,'system','_self',0,NULL,NULL,1),(13,'main-menu',3,4,0,NULL,'/d/hook-tags','','Hook Tags',NULL,'menu','_self',0,NULL,NULL,1),(17,'main-menu',5,6,0,NULL,'/d/about','','About',NULL,'menu','_self',0,NULL,NULL,1),(18,'management',21,22,0,NULL,'/admin/locale','','Languages',NULL,'locale','_self',0,NULL,NULL,1),(21,'main-menu',1,2,0,NULL,'/','','Home',NULL,'menu','_self',0,NULL,NULL,1),(22,'user-menu',1,2,0,NULL,'/user/my_account','','My account',NULL,'menu','_self',0,NULL,NULL,1),(23,'user-menu',3,4,0,NULL,'/user/logout','','Logout',NULL,'menu','_self',0,NULL,NULL,1); -/*!40000 ALTER TABLE `qa_menu_links` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_menus` --- - -DROP TABLE IF EXISTS `qa_menus`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_menus` ( - `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.', - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Menu title; displayed at top of block.', - `description` text COLLATE utf8_unicode_ci COMMENT 'Menu description.', - `module` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_menus` --- - -LOCK TABLES `qa_menus` WRITE; -/*!40000 ALTER TABLE `qa_menus` DISABLE KEYS */; -INSERT INTO `qa_menus` VALUES ('main-menu','Main menu','The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.','system'),('management','Management','The Management menu contains links for administrative tasks.','system'),('navigation','Navigation','The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.','system'),('user-menu','User menu','The User menu contains links related to the user\'s account, as well as the \'Log out\' link.','system'); -/*!40000 ALTER TABLE `qa_menus` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_modules` --- - -DROP TABLE IF EXISTS `qa_modules`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_modules` ( - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'machine name', - `type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'module' COMMENT 'module or theme', - `settings` text COLLATE utf8_unicode_ci COMMENT 'serialized extra data', - `status` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_modules` --- - -LOCK TABLES `qa_modules` WRITE; -/*!40000 ALTER TABLE `qa_modules` DISABLE KEYS */; -INSERT INTO `qa_modules` VALUES ('block','module','',1),('comment','module','',1),('field','module','',1),('locale','module','',1),('menu','module','',1),('node','module','',1),('system','module','',1),('taxonomy','module','',1),('theme_admin_default','theme','a:4:{s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"1\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";}',1),('theme_default','theme','a:7:{s:13:\"slider_folder\";s:6:\"slider\";s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"0\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";s:16:\"color_header_top\";s:7:\"#282727\";s:19:\"color_header_bottom\";s:7:\"#332f2f\";}',1),('user','module','',1); -/*!40000 ALTER TABLE `qa_modules` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_node_types` --- - -DROP TABLE IF EXISTS `qa_node_types`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_node_types` ( - `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The machine-readable name of this type.', - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The human-readable name of this type.', - `base` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The base string used to construct callbacks corresponding to this node type.', - `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The module defining this node type.', - `description` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'A brief description of this type.', - `title_label` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The label displayed for the title field on the edit form.', - `comments_approve` tinyint(1) DEFAULT '0', - `comments_per_page` int(4) NOT NULL DEFAULT '10', - `comments_anonymous` tinyint(3) NOT NULL DEFAULT '0', - `comments_subject_field` tinyint(1) NOT NULL DEFAULT '1', - `node_show_author` tinyint(1) DEFAULT '1', - `node_show_date` tinyint(1) DEFAULT '1', - `default_comment` int(11) DEFAULT NULL, - `default_language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, - `default_status` int(11) DEFAULT NULL, - `default_promote` int(11) DEFAULT NULL, - `default_sticky` int(11) DEFAULT NULL, - `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'A boolean indicating whether the node type is disabled.', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores information about all defined node types.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_node_types` --- - -LOCK TABLES `qa_node_types` WRITE; -/*!40000 ALTER TABLE `qa_node_types` DISABLE KEYS */; -INSERT INTO `qa_node_types` VALUES ('page','Basic page','node_content','system','Use basic pages for your static content, such as an \'About us\' page.','Title',1,10,2,1,0,0,0,'es',1,0,0,1); -/*!40000 ALTER TABLE `qa_node_types` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_nodes` --- - -DROP TABLE IF EXISTS `qa_nodes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_nodes` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a node.', - `node_type_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The node_type.type of this node.', - `node_type_base` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'performance data for models', - `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The languages.language of this node.', - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The title of this node, always treated as non-markup plain text.', - `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `slug` text COLLATE utf8_unicode_ci NOT NULL, - `terms_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', - `roles_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', - `created_by` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid that owns this node; initially, this is the user that created it.', - `status` int(11) NOT NULL DEFAULT '1' COMMENT 'Boolean indicating whether the node is published (visible to non-administrators).', - `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was created.', - `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was most recently saved.', - `modified_by` int(11) DEFAULT NULL, - `comment` int(11) NOT NULL DEFAULT '0' COMMENT 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).', - `comment_count` int(11) DEFAULT '0', - `promote` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed on the front page.', - `sticky` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.', - `cache` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, - `params` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`,`node_type_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='The base table for nodes.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_nodes` --- - -LOCK TABLES `qa_nodes` WRITE; -/*!40000 ALTER TABLE `qa_nodes` DISABLE KEYS */; -INSERT INTO `qa_nodes` VALUES (1,'page','node_content','','Hook Tags','','hook-tags','','',1,1,1310424311,1310424311,1,0,0,0,0,'',NULL),(2,'page','node_content','','About','','about','','',1,1,1310424311,1310424311,1,0,1,1,0,'',NULL); -/*!40000 ALTER TABLE `qa_nodes` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_nodes_roles` --- - -DROP TABLE IF EXISTS `qa_nodes_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_nodes_roles` ( - `node_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, - `role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from roles.id.', - PRIMARY KEY (`node_id`,`role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_nodes_roles` --- - -LOCK TABLES `qa_nodes_roles` WRITE; -/*!40000 ALTER TABLE `qa_nodes_roles` DISABLE KEYS */; -INSERT INTO `qa_nodes_roles` VALUES ('1',0); -/*!40000 ALTER TABLE `qa_nodes_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_nodes_terms` --- - -DROP TABLE IF EXISTS `qa_nodes_terms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_nodes_terms` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `node_id` int(20) NOT NULL DEFAULT '0', - `term_id` int(20) NOT NULL DEFAULT '0', - `field_id` int(11) NOT NULL DEFAULT '0' COMMENT 'field instance''s ID which creates this tag assoc.', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_nodes_terms` --- - -LOCK TABLES `qa_nodes_terms` WRITE; -/*!40000 ALTER TABLE `qa_nodes_terms` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_nodes_terms` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_roles` --- - -DROP TABLE IF EXISTS `qa_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_roles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - `ordering` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_roles` --- - -LOCK TABLES `qa_roles` WRITE; -/*!40000 ALTER TABLE `qa_roles` DISABLE KEYS */; -INSERT INTO `qa_roles` VALUES (1,'administrator',1),(2,'authenticated user',2),(3,'anonymous user',3); -/*!40000 ALTER TABLE `qa_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_terms` --- - -DROP TABLE IF EXISTS `qa_terms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_terms` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `vocabulary_id` int(11) NOT NULL, - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `description` text COLLATE utf8_unicode_ci, - `modified` int(11) NOT NULL, - `created` int(11) NOT NULL, - `parent_id` int(11) DEFAULT '0', - `lft` int(11) NOT NULL, - `rght` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `slug` (`slug`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_terms` --- - -LOCK TABLES `qa_terms` WRITE; -/*!40000 ALTER TABLE `qa_terms` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_terms` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_translations` --- - -DROP TABLE IF EXISTS `qa_translations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_translations` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `original` text COLLATE utf8_unicode_ci NOT NULL, - `created` int(11) NOT NULL, - `created_by` int(11) NOT NULL, - `modified` int(11) NOT NULL, - `modified_by` int(11) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_translations` --- - -LOCK TABLES `qa_translations` WRITE; -/*!40000 ALTER TABLE `qa_translations` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_translations` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_users` --- - -DROP TABLE IF EXISTS `qa_users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(60) COLLATE utf8_unicode_ci NOT NULL, - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `public_email` tinyint(1) NOT NULL DEFAULT '0', - `avatar` tinytext COLLATE utf8_unicode_ci COMMENT 'full url to avatar image file', - `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, - `timezone` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, - `key` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `status` tinyint(4) NOT NULL DEFAULT '0', - `created` int(11) NOT NULL, - `modified` int(11) NOT NULL, - `last_login` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_users` --- - -LOCK TABLES `qa_users` WRITE; -/*!40000 ALTER TABLE `qa_users` DISABLE KEYS */; -INSERT INTO `qa_users` VALUES (1,'admin','QuickApps','f6ab52454037ee501824bf30e2fb0544edb36c77','info@quickapps.es',0,'','','','4e46f0b7-bb50-4587-9217-14bc22b50a39',1,1313271991,1313271991,1313773796); -/*!40000 ALTER TABLE `qa_users` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_users_roles` --- - -DROP TABLE IF EXISTS `qa_users_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_users_roles` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `role_id` int(11) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='User HABTM Role'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_users_roles` --- - -LOCK TABLES `qa_users_roles` WRITE; -/*!40000 ALTER TABLE `qa_users_roles` DISABLE KEYS */; -INSERT INTO `qa_users_roles` VALUES (8,1,1); -/*!40000 ALTER TABLE `qa_users_roles` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_variables` --- - -DROP TABLE IF EXISTS `qa_variables`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_variables` ( - `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - `value` text COLLATE utf8_unicode_ci, - UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_variables` --- - -LOCK TABLES `qa_variables` WRITE; -/*!40000 ALTER TABLE `qa_variables` DISABLE KEYS */; -INSERT INTO `qa_variables` VALUES ('admin_theme','s:12:\"AdminDefault\";'),('date_default_timezone','s:13:\"Europe/Madrid\";'),('default_language','s:3:\"eng\";'),('default_nodes_main','s:1:\"8\";'),('failed_login_limit','i:5;'),('rows_per_page','i:10;'),('site_description','a:0:{}'),('site_frontpage','a:0:{}'),('site_logo','s:8:\"logo.gif\";'),('site_mail','s:24:\"no-reply@your-domain.com\";'),('site_name','s:17:\"My QuickApps Site\";'),('site_online','s:1:\"1\";'),('site_slogan','s:142:\"Vivamus id feugiat ligula. Nulla facilisi. Integer lacus justo, elementum eget consequat a, molestie nec sapien. Quisque tincidunt, nunc vitae\";'),('site_theme','s:7:\"Default\";'),('user_default_avatar','s:25:\"/img/anonymous_avatar.jpg\";'),('user_mail_activation_body','s:246:\"[user_name],\r\n\r\nYour account at [site_name] has been activated.\r\n\r\nYou may now log in by clicking this link or copying and pasting it into your browser:\r\n\r\n[site_login_url]\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_activation_notify','s:1:\"1\";'),('user_mail_activation_subject','s:57:\"Account details for [user_name] at [site_name] (approved)\";'),('user_mail_blocked_body','s:85:\"[user_name],\r\n\r\nYour account on [site_name] has been blocked.\r\n\r\n-- [site_name] team\";'),('user_mail_blocked_notify','s:1:\"1\";'),('user_mail_blocked_subject','s:56:\"Account details for [user_name] at [site_name] (blocked)\";'),('user_mail_canceled_body','s:86:\"[user_name],\r\n\r\nYour account on [site_name] has been canceled.\r\n\r\n-- [site_name] team\";'),('user_mail_canceled_notify','s:1:\"1\";'),('user_mail_canceled_subject','s:57:\"Account details for [user_name] at [site_name] (canceled)\";'),('user_mail_password_recovery_body','s:273:\"[user_name],\r\n\r\nA request to reset the password for your account has been made at [site_name].\r\nYou may now log in by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nAfter log in you can reset your password.\r\n\r\n-- [site_name] team\";'),('user_mail_password_recovery_subject','s:60:\"Replacement login information for [user_name] at [site_name]\";'),('user_mail_welcome_body','s:301:\"[user_name],\r\n\r\nThank you for registering at [site_name]. You may now activate your account by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nThis link can only be used once to log in.\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_welcome_subject','s:46:\"Account details for [user_name] at [site_name]\";'); -/*!40000 ALTER TABLE `qa_variables` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `qa_vocabularies` --- - -DROP TABLE IF EXISTS `qa_vocabularies`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `qa_vocabularies` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `description` text COLLATE utf8_unicode_ci, - `required` tinyint(1) NOT NULL DEFAULT '0', - `ordering` int(11) DEFAULT '0', - `modified` int(11) NOT NULL, - `created` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `slug` (`slug`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `qa_vocabularies` --- - -LOCK TABLES `qa_vocabularies` WRITE; -/*!40000 ALTER TABLE `qa_vocabularies` DISABLE KEYS */; -/*!40000 ALTER TABLE `qa_vocabularies` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-30 10:29:14 diff --git a/app/Config/Schema/tables/acos.sql b/app/Config/Schema/tables/acos.sql deleted file mode 100644 index 9bec64cc..00000000 --- a/app/Config/Schema/tables/acos.sql +++ /dev/null @@ -1,56 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__acos` --- - -DROP TABLE IF EXISTS `#__acos`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__acos` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `parent_id` int(10) DEFAULT NULL, - `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `foreign_key` int(10) DEFAULT NULL, - `alias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `lft` int(10) DEFAULT NULL, - `rght` int(10) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=150 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__acos` --- - -LOCK TABLES `#__acos` WRITE; -/*!40000 ALTER TABLE `#__acos` DISABLE KEYS */; -INSERT INTO `#__acos` VALUES (1,NULL,NULL,NULL,'Block',1,20),(2,1,NULL,NULL,'Block',2,5),(3,2,NULL,NULL,'admin_index',3,4),(4,1,NULL,NULL,'Manage',6,19),(5,4,NULL,NULL,'admin_index',7,8),(6,4,NULL,NULL,'admin_move',9,10),(7,4,NULL,NULL,'admin_clone',11,12),(8,4,NULL,NULL,'admin_edit',13,14),(9,4,NULL,NULL,'admin_add',15,16),(10,4,NULL,NULL,'admin_delete',17,18),(11,NULL,NULL,NULL,'Comment',21,34),(12,11,NULL,NULL,'Comment',22,25),(13,12,NULL,NULL,'admin_index',23,24),(14,11,NULL,NULL,'Published',26,29),(15,14,NULL,NULL,'admin_index',27,28),(16,11,NULL,NULL,'Unpublished',30,33),(17,16,NULL,NULL,'admin_index',31,32),(18,NULL,NULL,NULL,'Field',35,42),(19,18,NULL,NULL,'Handler',36,41),(20,19,NULL,NULL,'admin_delete',37,38),(21,19,NULL,NULL,'admin_move',39,40),(22,NULL,NULL,NULL,'FieldFile',43,50),(23,22,NULL,NULL,'Uploadify',44,49),(24,23,NULL,NULL,'delete',45,46),(25,23,NULL,NULL,'upload',47,48),(26,NULL,NULL,NULL,'Locale',51,90),(27,26,NULL,NULL,'Languages',52,63),(28,27,NULL,NULL,'admin_index',53,54),(29,27,NULL,NULL,'admin_set_default',55,56),(30,27,NULL,NULL,'admin_add',57,58),(31,27,NULL,NULL,'admin_edit',59,60),(32,27,NULL,NULL,'admin_delete',61,62),(33,26,NULL,NULL,'Locale',64,67),(34,33,NULL,NULL,'admin_index',65,66),(35,26,NULL,NULL,'Packages',68,77),(36,35,NULL,NULL,'admin_index',69,70),(37,35,NULL,NULL,'admin_download_package',71,72),(38,35,NULL,NULL,'admin_uninstall',73,74),(39,35,NULL,NULL,'admin_install',75,76),(40,26,NULL,NULL,'Translations',78,89),(41,40,NULL,NULL,'admin_index',79,80),(42,40,NULL,NULL,'admin_list',81,82),(43,40,NULL,NULL,'admin_edit',83,84),(44,40,NULL,NULL,'admin_add',85,86),(45,40,NULL,NULL,'admin_delete',87,88),(46,NULL,NULL,NULL,'Menu',91,114),(47,46,NULL,NULL,'Manage',92,109),(48,47,NULL,NULL,'admin_index',93,94),(49,47,NULL,NULL,'admin_delete',95,96),(50,47,NULL,NULL,'admin_add',97,98),(51,47,NULL,NULL,'admin_edit',99,100),(52,47,NULL,NULL,'admin_delete_link',101,102),(53,47,NULL,NULL,'admin_add_link',103,104),(54,47,NULL,NULL,'admin_edit_link',105,106),(55,47,NULL,NULL,'admin_links',107,108),(56,46,NULL,NULL,'Menu',110,113),(57,56,NULL,NULL,'admin_index',111,112),(58,NULL,NULL,NULL,'Node',115,158),(59,58,NULL,NULL,'Contents',116,129),(60,59,NULL,NULL,'admin_index',117,118),(61,59,NULL,NULL,'admin_edit',119,120),(62,59,NULL,NULL,'admin_create',121,122),(63,59,NULL,NULL,'admin_add',123,124),(64,59,NULL,NULL,'admin_delete',125,126),(65,59,NULL,NULL,'admin_clear_cache',127,128),(66,58,NULL,NULL,'Node',130,139),(67,66,NULL,NULL,'admin_index',131,132),(68,66,NULL,NULL,'index',133,134),(69,66,NULL,NULL,'details',135,136),(70,66,NULL,NULL,'search',137,138),(71,58,NULL,NULL,'Types',140,157),(72,71,NULL,NULL,'admin_index',141,142),(73,71,NULL,NULL,'admin_edit',143,144),(74,71,NULL,NULL,'admin_add',145,146),(75,71,NULL,NULL,'admin_delete',147,148),(76,71,NULL,NULL,'admin_display',149,150),(77,71,NULL,NULL,'admin_field_settings',151,152),(78,71,NULL,NULL,'admin_field_formatter',153,154),(79,71,NULL,NULL,'admin_fields',155,156),(80,NULL,NULL,NULL,'System',159,208),(81,80,NULL,NULL,'Configuration',160,163),(82,81,NULL,NULL,'admin_index',161,162),(83,80,NULL,NULL,'Dashboard',164,167),(84,83,NULL,NULL,'admin_index',165,166),(85,80,NULL,NULL,'Help',168,173),(86,85,NULL,NULL,'admin_index',169,170),(87,85,NULL,NULL,'admin_module',171,172),(88,80,NULL,NULL,'Modules',174,185),(89,88,NULL,NULL,'admin_index',175,176),(90,88,NULL,NULL,'admin_settings',177,178),(91,88,NULL,NULL,'admin_toggle',179,180),(92,88,NULL,NULL,'admin_uninstall',181,182),(93,88,NULL,NULL,'admin_install',183,184),(94,80,NULL,NULL,'Structure',186,189),(95,94,NULL,NULL,'admin_index',187,188),(96,80,NULL,NULL,'System',190,193),(97,96,NULL,NULL,'admin_index',191,192),(98,80,NULL,NULL,'Themes',194,207),(99,98,NULL,NULL,'admin_index',195,196),(100,98,NULL,NULL,'admin_set_theme',197,198),(101,98,NULL,NULL,'admin_settings',199,200),(102,98,NULL,NULL,'admin_uninstall',201,202),(103,98,NULL,NULL,'admin_install',203,204),(104,98,NULL,NULL,'admin_theme_tn',205,206),(105,NULL,NULL,NULL,'Taxonomy',209,232),(106,105,NULL,NULL,'Taxonomy',210,213),(107,106,NULL,NULL,'admin_index',211,212),(108,105,NULL,NULL,'Vocabularies',214,231),(109,108,NULL,NULL,'admin_index',215,216),(110,108,NULL,NULL,'admin_add',217,218),(111,108,NULL,NULL,'admin_move',219,220),(112,108,NULL,NULL,'admin_edit',221,222),(113,108,NULL,NULL,'admin_delete',223,224),(114,108,NULL,NULL,'admin_terms',225,226),(115,108,NULL,NULL,'admin_delete_term',227,228),(116,108,NULL,NULL,'admin_edit_term',229,230),(117,NULL,NULL,NULL,'User',233,298),(118,117,NULL,NULL,'Display',234,239),(119,118,NULL,NULL,'admin_index',235,236),(120,118,NULL,NULL,'admin_field_formatter',237,238),(121,117,NULL,NULL,'Fields',240,245),(122,121,NULL,NULL,'admin_index',241,242),(123,121,NULL,NULL,'admin_field_settings',243,244),(124,117,NULL,NULL,'List',246,259),(125,124,NULL,NULL,'admin_index',247,248),(126,124,NULL,NULL,'admin_delete',249,250),(127,124,NULL,NULL,'admin_block',251,252),(128,124,NULL,NULL,'admin_activate',253,254),(129,124,NULL,NULL,'admin_add',255,256),(130,124,NULL,NULL,'admin_edit',257,258),(131,117,NULL,NULL,'Permissions',260,267),(132,131,NULL,NULL,'admin_index',261,262),(133,131,NULL,NULL,'admin_edit',263,264),(134,131,NULL,NULL,'admin_toggle',265,266),(135,117,NULL,NULL,'Roles',268,275),(136,135,NULL,NULL,'admin_index',269,270),(137,135,NULL,NULL,'admin_edit',271,272),(138,135,NULL,NULL,'admin_delete',273,274),(139,117,NULL,NULL,'User',276,297),(140,139,NULL,NULL,'admin_index',277,278),(141,139,NULL,NULL,'login',279,280),(142,139,NULL,NULL,'logout',281,282),(143,139,NULL,NULL,'admin_login',283,284),(144,139,NULL,NULL,'admin_logout',285,286),(145,139,NULL,NULL,'register',287,288),(146,139,NULL,NULL,'activate',289,290),(147,139,NULL,NULL,'password_recovery',291,292),(148,139,NULL,NULL,'profile',293,294),(149,139,NULL,NULL,'my_account',295,296); -/*!40000 ALTER TABLE `#__acos` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-28 22:45:25 diff --git a/app/Config/Schema/tables/aros.sql b/app/Config/Schema/tables/aros.sql deleted file mode 100644 index a8ee1529..00000000 --- a/app/Config/Schema/tables/aros.sql +++ /dev/null @@ -1,56 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__aros` --- - -DROP TABLE IF EXISTS `#__aros`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__aros` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `parent_id` int(10) DEFAULT NULL, - `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `foreign_key` int(10) DEFAULT NULL, - `alias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `lft` int(10) DEFAULT NULL, - `rght` int(10) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__aros` --- - -LOCK TABLES `#__aros` WRITE; -/*!40000 ALTER TABLE `#__aros` DISABLE KEYS */; -INSERT INTO `#__aros` VALUES (1,NULL,'User.Role',1,NULL,1,2),(2,NULL,'User.Role',2,NULL,3,4),(3,NULL,'User.Role',3,NULL,5,6); -/*!40000 ALTER TABLE `#__aros` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:45 diff --git a/app/Config/Schema/tables/aros_acos.sql b/app/Config/Schema/tables/aros_acos.sql deleted file mode 100644 index 94b20cf2..00000000 --- a/app/Config/Schema/tables/aros_acos.sql +++ /dev/null @@ -1,56 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__aros_acos` --- - -DROP TABLE IF EXISTS `#__aros_acos`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__aros_acos` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `aro_id` int(10) NOT NULL, - `aco_id` int(10) NOT NULL, - `_create` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', - `_read` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', - `_update` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', - `_delete` varchar(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__aros_acos` --- - -LOCK TABLES `#__aros_acos` WRITE; -/*!40000 ALTER TABLE `#__aros_acos` DISABLE KEYS */; -INSERT INTO `#__aros_acos` VALUES (1,3,68,'1','1','1','1'),(2,2,68,'1','1','1','1'),(3,3,69,'1','1','1','1'),(4,2,69,'1','1','1','1'),(5,3,70,'1','1','1','1'),(6,2,70,'1','1','1','1'),(7,3,141,'1','1','1','1'),(8,2,142,'1','1','1','1'),(9,3,143,'1','1','1','1'),(10,2,144,'1','1','1','1'),(11,3,145,'1','1','1','1'),(12,3,146,'1','1','1','1'),(13,2,146,'1','1','1','1'),(14,2,147,'1','1','1','1'),(15,3,147,'1','1','1','1'),(16,2,148,'1','1','1','1'),(17,2,149,'1','1','1','1'); -/*!40000 ALTER TABLE `#__aros_acos` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-28 22:45:25 diff --git a/app/Config/Schema/tables/block_custom.sql b/app/Config/Schema/tables/block_custom.sql deleted file mode 100644 index b7477216..00000000 --- a/app/Config/Schema/tables/block_custom.sql +++ /dev/null @@ -1,53 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__block_custom` --- - -DROP TABLE IF EXISTS `#__block_custom`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__block_custom` ( - `block_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The block’s block.bid.', - `body` longtext COLLATE utf8_unicode_ci COMMENT 'Block contents.', - `description` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Block description.', - `format` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The filter_format.format of the block body.', - PRIMARY KEY (`block_id`) -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores contents of custom-made blocks.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__block_custom` --- - -LOCK TABLES `#__block_custom` WRITE; -/*!40000 ALTER TABLE `#__block_custom` DISABLE KEYS */; -INSERT INTO `#__block_custom` VALUES (11,'

    Duis tellus nunc, egestas a interdum sed, congue vitae magna. Curabitur a tellus quis lacus blandit sagittis a sit amet elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas metus sed neque ultricies.\r\n[button size=small color=silver]Read more[/button]

    \r\n','Services-LEFT',NULL),(12,'

    Integer egestas ultricies urna vitae molestie. Donec nec facilisis nisi. Vivamus tempor feugiat velit gravida vehicula. Donec faucibus pellentesque ipsum id varius. Ut rutrum metus sed neque ultricies a dictum ante sagittis.\r\n[button size=small color=silver]Read more[/button]

    \r\n','Services-CENTER',NULL),(13,'

    Praesent et metus sit amet nisl luctus commodo ut a risus. Mauris vehicula, ligula quis consectetur feugiat, arcu nibh tempor nisi, at varius dolor dolor nec dolor. Donec auctor mi vitae neque. Praesent sollicitudin egestas felis vitae gravida.\r\n[button size=small color=silver]Read more[/button]\r\n

    \r\n','Services-RIGHT',NULL); -/*!40000 ALTER TABLE `#__block_custom` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:45 diff --git a/app/Config/Schema/tables/block_regions.sql b/app/Config/Schema/tables/block_regions.sql deleted file mode 100644 index ab06cb09..00000000 --- a/app/Config/Schema/tables/block_regions.sql +++ /dev/null @@ -1,54 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__block_regions` --- - -DROP TABLE IF EXISTS `#__block_regions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__block_regions` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `block_id` int(11) NOT NULL, - `theme` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `region` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `ordering` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`,`block_id`) -) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__block_regions` --- - -LOCK TABLES `#__block_regions` WRITE; -/*!40000 ALTER TABLE `#__block_regions` DISABLE KEYS */; -INSERT INTO `#__block_regions` VALUES (8,10,'AdminDefault','dashboard_main',1),(9,10,'Default','dashboard_main',1),(13,4,'AdminDefault','management-menu',1),(14,4,'Default','management-menu',1),(18,3,'AdminDefault','footer',1),(48,3,'Default','footer',1),(131,6,'Default','main-menu',1),(133,26,'Default','slider',1),(140,9,'Default','language-switcher',1),(151,19,'Default','services-left',1),(153,20,'Default','services-center',1),(155,25,'Default','services-right',1),(157,15,'Default','search',1),(159,12,'Default','services-left',2),(161,11,'Default','services-center',2),(163,13,'Default','services-right',2),(165,14,'Default','slider',2),(166,15,'AdminDefault','dashboard_sidebar',1),(169,7,'AdminDefault','dashboard_sidebar',2),(172,5,'Default','user-menu',2); -/*!40000 ALTER TABLE `#__block_regions` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/block_roles.sql b/app/Config/Schema/tables/block_roles.sql deleted file mode 100644 index d2447aa3..00000000 --- a/app/Config/Schema/tables/block_roles.sql +++ /dev/null @@ -1,51 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__block_roles` --- - -DROP TABLE IF EXISTS `#__block_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__block_roles` ( - `block_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, - `user_role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from users_roles.rid.', - PRIMARY KEY (`block_id`,`user_role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__block_roles` --- - -LOCK TABLES `#__block_roles` WRITE; -/*!40000 ALTER TABLE `#__block_roles` DISABLE KEYS */; -INSERT INTO `#__block_roles` VALUES ('1',3),('5',2); -/*!40000 ALTER TABLE `#__block_roles` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/blocks.sql b/app/Config/Schema/tables/blocks.sql deleted file mode 100644 index 623c5940..00000000 --- a/app/Config/Schema/tables/blocks.sql +++ /dev/null @@ -1,62 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__blocks` --- - -DROP TABLE IF EXISTS `#__blocks`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__blocks` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key - Unique block ID.', - `module` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The module from which the block originates; for example, ’user’ for the Who’s Online block, and ’block’ for any custom blocks.', - `delta` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'Unique ID for block within a module. Or menu_id', - `clone_of` int(11) NOT NULL DEFAULT '0', - `themes_cache` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'store all themes that belongs to (see block_regions table)', - `ordering` int(11) NOT NULL DEFAULT '1', - `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT 'Block enabled status. (1 = enabled, 0 = disabled)', - `visibility` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Flag to indicate how to show blocks on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)', - `pages` text COLLATE utf8_unicode_ci COMMENT 'Contents of the "Pages" block; contains either a list of paths on which to include/exclude the block or PHP code, depending on "visibility" setting.', - `title` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Custom title for the block. (Empty string will use block default title, will remove the title, text will cause block to use specified title.)', - `locale` text COLLATE utf8_unicode_ci, - `settings` text COLLATE utf8_unicode_ci, - `params` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores block settings, such as region and visibility...'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__blocks` --- - -LOCK TABLES `#__blocks` WRITE; -/*!40000 ALTER TABLE `#__blocks` DISABLE KEYS */; -INSERT INTO `#__blocks` VALUES (1,'user','login',0,'',1,0,0,'','User Login','a:0:{}','',NULL),(2,'menu','navigation',0,':Default:',2,1,0,'','',NULL,'',NULL),(3,'system','powered_by',0,':AdminDefault:Default:',1,1,0,'','Powered By','a:0:{}','',NULL),(4,'menu','management',0,':AdminDefault:',1,1,1,'/admin/*','','a:0:{}','',NULL),(5,'menu','user-menu',0,':Default:',4,1,0,'','User Menu','a:0:{}','',NULL),(6,'menu','main-menu',0,':Default:',1,1,0,'','','a:0:{}','',NULL),(7,'user','new',0,':AdminDefault:',5,1,0,'','New Users','a:0:{}','a:1:{s:10:\"show_limit\";s:1:\"5\";}',NULL),(9,'locale','language_switcher',0,':Default:',3,1,0,'','Language switcher','a:0:{}','a:2:{s:5:\"flags\";s:1:\"1\";s:4:\"name\";s:1:\"1\";}',NULL),(10,'system','recent_content',0,':AdminDefault:',1,1,0,'','Updates','a:0:{}','',NULL),(11,'block','5',0,':Default:',1,1,0,'','WHAT WE DO','a:0:{}','',NULL),(12,'block','6',0,':Default:',1,1,0,'','OUR MISSION','a:0:{}','',NULL),(13,'block','7',0,':Default:',1,1,0,'','WHO WE ARE','a:0:{}','',NULL),(14,'theme_default','slider',0,':Default:',1,1,1,'/','Slider','a:0:{}','a:1:{s:12:\"slider_order\";s:52:\"1_[language].jpg\r\n2_[language].jpg\r\n3_[language].jpg\";}',NULL),(15,'node','search',0,':AdminDefault:Default:',1,1,0,'','Search','a:0:{}','',NULL),(16,'taxonomy','vocabularies',0,NULL,1,1,0,NULL,'Vocabularies',NULL,NULL,NULL); -/*!40000 ALTER TABLE `#__blocks` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-13 16:46:05 diff --git a/app/Config/Schema/tables/comments.sql b/app/Config/Schema/tables/comments.sql deleted file mode 100644 index 9454a550..00000000 --- a/app/Config/Schema/tables/comments.sql +++ /dev/null @@ -1,60 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__comments` --- - -DROP TABLE IF EXISTS `#__comments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__comments` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key: Unique comment ID.', - `node_id` int(11) NOT NULL COMMENT 'The node.nid to which this comment is a reply.', - `user_id` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid who authored the comment. If set to 0, this comment was created by an anonymous user.', - `body` text COLLATE utf8_unicode_ci, - `subject` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `hostname` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The author’s host name. (IP)', - `homepage` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was created, as a Unix timestamp.', - `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The time that the comment was last edited, as a Unix timestamp.', - `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT 'The published status of a comment. (0 = Not Published, 1 = Published)', - `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s name. Uses users.name if the user is logged in, otherwise uses the value typed into the comment form.', - `mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The comment author’s e-mail address from the comment form, if user is anonymous, and the ’Anonymous users may/must leave their contact information’ setting is turned on.', - PRIMARY KEY (`id`,`node_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores comments and associated data.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__comments` --- - -LOCK TABLES `#__comments` WRITE; -/*!40000 ALTER TABLE `#__comments` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__comments` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/field_data.sql b/app/Config/Schema/tables/field_data.sql deleted file mode 100644 index 2d688082..00000000 --- a/app/Config/Schema/tables/field_data.sql +++ /dev/null @@ -1,55 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__field_data` --- - -DROP TABLE IF EXISTS `#__field_data`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__field_data` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `field_id` int(11) NOT NULL, - `foreignKey` varchar(20) COLLATE utf8_unicode_ci NOT NULL, - `belongsTo` varchar(100) COLLATE utf8_unicode_ci NOT NULL, - `data` longtext COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `field_id` (`field_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__field_data` --- - -LOCK TABLES `#__field_data` WRITE; -/*!40000 ALTER TABLE `#__field_data` DISABLE KEYS */; -INSERT INTO `#__field_data` VALUES (1,1,'1','Node','

    Content Boxes

    \r\n

    \r\n [content_box type=success]Maecenas pellentesque cursus auctor.[/content_box]

    \r\n

    \r\n [content_box type=error]Nam sagittis nisl non turpis aliquam mollis. Suspendisse ac metus nisi, sed vulputate arcu.[/content_box]

    \r\n

    \r\n [content_box type=alert]Cras interdum leo quis arcu sagittis pulvinar. Curabitur suscipit vulputate erat eu rhoncus. Morbi facilisis mi in ligula ornare ultricies.[/content_box]

    \r\n

    \r\n [content_box type=bubble]Fusce interdum cursus turpis vitae gravida. Aenean aliquet venenatis posuere. Etiam gravida ullamcorper purus.[/content_box]

    \r\n
    \r\n

    \r\n Buttons

    \r\n

    \r\n Using buttons hookTags, you can easily create a variety of buttons. These buttons all stem from a single tag, but vary in color and size (each of which are adjustable using color=”" and size=”" parameters).
    \r\n Allowed parameters:

    \r\n
      \r\n
    1. \r\n size: big, small
    2. \r\n
    3. \r\n color:\r\n
        \r\n
      • \r\n small: black, blue, green, lightblue, orange, pink, purple, red, silver, teal
      • \r\n
      • \r\n big: blue, green, orange, purple, red, turquoise
      • \r\n
      \r\n
    4. \r\n
    5. \r\n link: url of your button
    6. \r\n
    7. \r\n target: open link en new window (_blank), open in same window (_self or unset parameter)
    8. \r\n
    \r\n

    \r\n  

    \r\n

    \r\n  

    \r\n

    \r\n Small Buttons

    \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n [button color=black]Button text[/button]\r\n [button color=blue]Button text[/button]
    \r\n [button color=green]Button text[/button]\r\n [button color=lightblue]Button text[/button]
    \r\n [button color=orange]Button text[/button]\r\n [button color=pink]Button text[/button]
    \r\n [button color=purple]Button text[/button]\r\n [button color=red]Button text[/button]
    \r\n [button color=silver]Button text[/button]\r\n [button color=teal]Button text[/button]
    \r\n

    \r\n  

    \r\n

    \r\n  

    \r\n

    \r\n Big Buttons

    \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
    \r\n [button color=blue size=big]Button text[/button]\r\n [button color=green size=big]Button text[/button]
    \r\n [button color=orange size=big]Button text[/button]\r\n [button color=purple size=big]Button text[/button]
    \r\n [button color=red size=big]Button text[/button]\r\n [button color=turquoise size=big]Button text[/button]
    \r\n

    \r\n  

    \r\n'),(2,1,'2','Node','Nam in iaculis lectus? Sed egestas dui quis leo porttitor vitae bibendum ipsum ultrices. Mauris nisi nulla, volutpat vel vestibulum non, lobortis sed lectus. Integer quis volutpat.'); -/*!40000 ALTER TABLE `#__field_data` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-02 12:04:02 diff --git a/app/Config/Schema/tables/fields.sql b/app/Config/Schema/tables/fields.sql deleted file mode 100644 index 384204a6..00000000 --- a/app/Config/Schema/tables/fields.sql +++ /dev/null @@ -1,58 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__fields` --- - -DROP TABLE IF EXISTS `#__fields`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__fields` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a field', - `name` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The name of this field. Must be unique', - `label` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Human name', - `belongsTo` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL, - `field_module` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The module that implements the field object', - `description` text COLLATE utf8_unicode_ci, - `required` tinyint(1) NOT NULL DEFAULT '0', - `settings` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Rendering settings (View mode)', - `ordering` int(11) DEFAULT '1' COMMENT 'edit form ordering', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Fields instances'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__fields` --- - -LOCK TABLES `#__fields` WRITE; -/*!40000 ALTER TABLE `#__fields` DISABLE KEYS */; -INSERT INTO `#__fields` VALUES (1,'body','Body','NodeType-page','field_text','',1,'a:7:{s:7:\"display\";a:4:{s:7:\"default\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:1;s:11:\"trim_length\";s:3:\"180\";}s:4:\"full\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:4:\"full\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"600\";}s:4:\"list\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}s:3:\"rss\";a:5:{s:5:\"label\";s:6:\"hidden\";s:4:\"type\";s:7:\"trimmed\";s:8:\"settings\";a:0:{}s:8:\"ordering\";i:0;s:11:\"trim_length\";s:3:\"400\";}}s:4:\"type\";s:8:\"textarea\";s:11:\"text_format\";s:4:\"full\";s:7:\"max_len\";s:0:\"\";s:15:\"validation_rule\";s:0:\"\";s:18:\"validation_message\";s:0:\"\";s:15:\"text_processing\";s:4:\"full\";}',1); -/*!40000 ALTER TABLE `#__fields` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/i18n.sql b/app/Config/Schema/tables/i18n.sql deleted file mode 100644 index f3c8c0c4..00000000 --- a/app/Config/Schema/tables/i18n.sql +++ /dev/null @@ -1,58 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__i18n` --- - -DROP TABLE IF EXISTS `#__i18n`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__i18n` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `locale` varchar(6) COLLATE utf8_unicode_ci NOT NULL, - `model` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `foreign_key` int(10) NOT NULL, - `field` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `content` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`), - KEY `locale` (`locale`), - KEY `model` (`model`), - KEY `row_id` (`foreign_key`), - KEY `field` (`field`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__i18n` --- - -LOCK TABLES `#__i18n` WRITE; -/*!40000 ALTER TABLE `#__i18n` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__i18n` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-02 12:11:33 diff --git a/app/Config/Schema/tables/languages.sql b/app/Config/Schema/tables/languages.sql deleted file mode 100644 index 790bdd00..00000000 --- a/app/Config/Schema/tables/languages.sql +++ /dev/null @@ -1,58 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__languages` --- - -DROP TABLE IF EXISTS `#__languages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__languages` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `code` varchar(12) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Language code, e.g. ’eng’', - `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Language name in English.', - `native` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Native language name.', - `direction` varchar(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'ltr' COMMENT 'Direction of language (Left-to-Right , Right-to-Left ).', - `icon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `status` int(11) NOT NULL DEFAULT '0' COMMENT 'Enabled flag (1 = Enabled, 0 = Disabled).', - `ordering` int(11) NOT NULL DEFAULT '0' COMMENT 'Weight, used in lists of languages.', - PRIMARY KEY (`id`), - UNIQUE KEY `code` (`code`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all available languages in the system.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__languages` --- - -LOCK TABLES `#__languages` WRITE; -/*!40000 ALTER TABLE `#__languages` DISABLE KEYS */; -INSERT INTO `#__languages` VALUES (1,'eng','English','English','ltr','us.gif',1,0),(2,'spa','Spanish','Español','ltr','es.gif',1,0); -/*!40000 ALTER TABLE `#__languages` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/menu_links.sql b/app/Config/Schema/tables/menu_links.sql deleted file mode 100644 index ed8fcd3d..00000000 --- a/app/Config/Schema/tables/menu_links.sql +++ /dev/null @@ -1,67 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__menu_links` --- - -DROP TABLE IF EXISTS `#__menu_links`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__menu_links` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `menu_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The menu name. All links with the same menu name (such as ’navigation’) are part of the same menu.', - `lft` int(11) NOT NULL, - `rght` int(11) NOT NULL, - `parent_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.', - `link_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'external path', - `router_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'internal path', - `description` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `link_title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The text displayed for the link, which may be modified by a title callback stored in menu_router.', - `options` text COLLATE utf8_unicode_ci COMMENT 'A serialized array of HTML attributes options.', - `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'system' COMMENT 'The name of the module that generated this link.', - `target` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT '_self', - `expanded` tinyint(6) NOT NULL DEFAULT '0' COMMENT 'Flag for whether this link should be rendered as expanded in menus - expanded links always have their child links displayed, instead of only when the link is in the active trail (1 = expanded, 0 = not expanded)', - `selected_on` text COLLATE utf8_unicode_ci COMMENT 'php code, or regular expression. based on selected_on_type', - `selected_on_type` varchar(5) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'php = on php return TRUE; reg = on URL match', - `status` tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (`id`), - KEY `router_path` (`router_path`(128)), - KEY `menu_id` (`menu_id`) -) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Contains the individual links within a menu.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__menu_links` --- - -LOCK TABLES `#__menu_links` WRITE; -/*!40000 ALTER TABLE `#__menu_links` DISABLE KEYS */; -INSERT INTO `#__menu_links` VALUES (1,'management',1,2,0,NULL,'/admin/system/dashboard',NULL,'Dashboard',NULL,'system','_self',0,NULL,NULL,1),(2,'management',3,12,0,NULL,'/admin/system/structure',NULL,'Structure',NULL,'system','_self',0,NULL,NULL,1),(3,'management',13,14,0,NULL,'/admin/node/contents',NULL,'Content',NULL,'system','_self',0,NULL,NULL,1),(4,'management',15,16,0,NULL,'/admin/system/themes',NULL,'Appearance',NULL,'system','_self',0,NULL,NULL,1),(5,'management',17,18,0,NULL,'/admin/system/modules',NULL,'Modules',NULL,'system','_self',0,NULL,NULL,1),(6,'management',19,20,0,NULL,'/admin/user',NULL,'Users',NULL,'system','_self',0,NULL,NULL,1),(7,'management',23,24,0,NULL,'/admin/system/configuration',NULL,'Configuration',NULL,'system','_self',0,NULL,NULL,1),(8,'management',25,26,0,NULL,'/admin/system/help',NULL,'Help',NULL,'system','_self',0,NULL,NULL,1),(9,'management',4,5,2,NULL,'/admin/block','Configure what block content appears in your site\'s sidebars and other regions.','Blocks',NULL,'system','_self',0,NULL,NULL,1),(10,'management',6,7,2,NULL,'/admin/node/types','Manage content types.','Content Types',NULL,'system','_self',0,NULL,NULL,1),(11,'management',8,9,2,NULL,'/admin/menu','Add new menus to your site, edit existing menus, and rename and reorganize menu links.','Menus',NULL,'system','_self',0,NULL,NULL,1),(12,'management',10,11,2,NULL,'/admin/taxonomy','Manage tagging, categorization, and classification of your content.','Taxonomy',NULL,'system','_self',0,NULL,NULL,1),(13,'main-menu',3,4,0,NULL,'/d/hook-tags','','Hook Tags',NULL,'menu','_self',0,NULL,NULL,1),(17,'main-menu',5,6,0,NULL,'/d/about','','About',NULL,'menu','_self',0,NULL,NULL,1),(18,'management',21,22,0,NULL,'/admin/locale','','Languages',NULL,'locale','_self',0,NULL,NULL,1),(21,'main-menu',1,2,0,NULL,'/','','Home',NULL,'menu','_self',0,NULL,NULL,1),(22,'user-menu',1,2,0,NULL,'/user/my_account','','My account',NULL,'menu','_self',0,NULL,NULL,1),(23,'user-menu',3,4,0,NULL,'/user/logout','','Logout',NULL,'menu','_self',0,NULL,NULL,1); -/*!40000 ALTER TABLE `#__menu_links` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-30 10:29:15 diff --git a/app/Config/Schema/tables/menus.sql b/app/Config/Schema/tables/menus.sql deleted file mode 100644 index 202c4666..00000000 --- a/app/Config/Schema/tables/menus.sql +++ /dev/null @@ -1,53 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__menus` --- - -DROP TABLE IF EXISTS `#__menus`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__menus` ( - `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique key for menu. This is used as a block delta so length is 32.', - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Menu title; displayed at top of block.', - `description` text COLLATE utf8_unicode_ci COMMENT 'Menu description.', - `module` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__menus` --- - -LOCK TABLES `#__menus` WRITE; -/*!40000 ALTER TABLE `#__menus` DISABLE KEYS */; -INSERT INTO `#__menus` VALUES ('main-menu','Main menu','The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.','system'),('management','Management','The Management menu contains links for administrative tasks.','system'),('navigation','Navigation','The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.','system'),('user-menu','User menu','The User menu contains links related to the user\'s account, as well as the \'Log out\' link.','system'); -/*!40000 ALTER TABLE `#__menus` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-12 13:32:32 diff --git a/app/Config/Schema/tables/modules.sql b/app/Config/Schema/tables/modules.sql deleted file mode 100644 index a0182f29..00000000 --- a/app/Config/Schema/tables/modules.sql +++ /dev/null @@ -1,53 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__modules` --- - -DROP TABLE IF EXISTS `#__modules`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__modules` ( - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'machine name', - `type` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'module' COMMENT 'module or theme', - `settings` text COLLATE utf8_unicode_ci COMMENT 'serialized extra data', - `status` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__modules` --- - -LOCK TABLES `#__modules` WRITE; -/*!40000 ALTER TABLE `#__modules` DISABLE KEYS */; -INSERT INTO `#__modules` VALUES ('block','module','',1),('comment','module','',1),('field','module','',1),('locale','module','',1),('menu','module','',1),('node','module','',1),('system','module','',1),('taxonomy','module','',1),('theme_admin_default','theme','a:4:{s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"1\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";}',1),('theme_default','theme','a:7:{s:13:\"slider_folder\";s:6:\"slider\";s:9:\"site_logo\";s:1:\"1\";s:9:\"site_name\";s:1:\"0\";s:11:\"site_slogan\";s:1:\"1\";s:12:\"site_favicon\";s:1:\"1\";s:16:\"color_header_top\";s:7:\"#282727\";s:19:\"color_header_bottom\";s:7:\"#332f2f\";}',1),('user','module','',1); -/*!40000 ALTER TABLE `#__modules` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/node_types.sql b/app/Config/Schema/tables/node_types.sql deleted file mode 100644 index 8bc95c77..00000000 --- a/app/Config/Schema/tables/node_types.sql +++ /dev/null @@ -1,67 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__node_types` --- - -DROP TABLE IF EXISTS `#__node_types`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__node_types` ( - `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The machine-readable name of this type.', - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The human-readable name of this type.', - `base` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The base string used to construct callbacks corresponding to this node type.', - `module` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The module defining this node type.', - `description` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'A brief description of this type.', - `title_label` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'The label displayed for the title field on the edit form.', - `comments_approve` tinyint(1) DEFAULT '0', - `comments_per_page` int(4) NOT NULL DEFAULT '10', - `comments_anonymous` tinyint(3) NOT NULL DEFAULT '0', - `comments_subject_field` tinyint(1) NOT NULL DEFAULT '1', - `node_show_author` tinyint(1) DEFAULT '1', - `node_show_date` tinyint(1) DEFAULT '1', - `default_comment` int(11) DEFAULT NULL, - `default_language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, - `default_status` int(11) DEFAULT NULL, - `default_promote` int(11) DEFAULT NULL, - `default_sticky` int(11) DEFAULT NULL, - `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'A boolean indicating whether the node type is disabled.', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Stores information about all defined node types.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__node_types` --- - -LOCK TABLES `#__node_types` WRITE; -/*!40000 ALTER TABLE `#__node_types` DISABLE KEYS */; -INSERT INTO `#__node_types` VALUES ('page','Basic page','node_content','system','Use basic pages for your static content, such as an \'About us\' page.','Title',1,10,2,1,0,0,0,'es',1,0,0,1); -/*!40000 ALTER TABLE `#__node_types` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/nodes.sql b/app/Config/Schema/tables/nodes.sql deleted file mode 100644 index 037ea8b2..00000000 --- a/app/Config/Schema/tables/nodes.sql +++ /dev/null @@ -1,69 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__nodes` --- - -DROP TABLE IF EXISTS `#__nodes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__nodes` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'The primary identifier for a node.', - `node_type_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The node_type.type of this node.', - `node_type_base` varchar(36) COLLATE utf8_unicode_ci NOT NULL COMMENT 'performance data for models', - `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The languages.language of this node.', - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'The title of this node, always treated as non-markup plain text.', - `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `slug` text COLLATE utf8_unicode_ci NOT NULL, - `terms_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', - `roles_cache` text COLLATE utf8_unicode_ci COMMENT 'serialized data for find performance', - `created_by` int(11) NOT NULL DEFAULT '0' COMMENT 'The users.uid that owns this node; initially, this is the user that created it.', - `status` int(11) NOT NULL DEFAULT '1' COMMENT 'Boolean indicating whether the node is published (visible to non-administrators).', - `created` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was created.', - `modified` int(11) NOT NULL DEFAULT '0' COMMENT 'The Unix timestamp when the node was most recently saved.', - `modified_by` int(11) DEFAULT NULL, - `comment` int(11) NOT NULL DEFAULT '0' COMMENT 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).', - `comment_count` int(11) DEFAULT '0', - `promote` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed on the front page.', - `sticky` int(11) NOT NULL DEFAULT '0' COMMENT 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.', - `cache` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, - `params` text COLLATE utf8_unicode_ci, - PRIMARY KEY (`id`,`node_type_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='The base table for nodes.'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__nodes` --- - -LOCK TABLES `#__nodes` WRITE; -/*!40000 ALTER TABLE `#__nodes` DISABLE KEYS */; -INSERT INTO `#__nodes` VALUES (1,'page','node_content','','Hook Tags','','hook-tags','','',1,1,1310424311,1310424311,1,0,0,0,0,'',NULL),(2,'page','node_content','','About','','about','','',1,1,1310424311,1310424311,1,0,1,1,0,'',NULL); -/*!40000 ALTER TABLE `#__nodes` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-09-13 16:46:05 diff --git a/app/Config/Schema/tables/nodes_roles.sql b/app/Config/Schema/tables/nodes_roles.sql deleted file mode 100644 index 039a67e5..00000000 --- a/app/Config/Schema/tables/nodes_roles.sql +++ /dev/null @@ -1,51 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__nodes_roles` --- - -DROP TABLE IF EXISTS `#__nodes_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__nodes_roles` ( - `node_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, - `role_id` int(10) unsigned NOT NULL COMMENT 'The user’s role ID from roles.id.', - PRIMARY KEY (`node_id`,`role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Sets up access permissions for blocks based on user roles'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__nodes_roles` --- - -LOCK TABLES `#__nodes_roles` WRITE; -/*!40000 ALTER TABLE `#__nodes_roles` DISABLE KEYS */; -INSERT INTO `#__nodes_roles` VALUES ('1',0); -/*!40000 ALTER TABLE `#__nodes_roles` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/nodes_terms.sql b/app/Config/Schema/tables/nodes_terms.sql deleted file mode 100644 index cbb61fb1..00000000 --- a/app/Config/Schema/tables/nodes_terms.sql +++ /dev/null @@ -1,52 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__nodes_terms` --- - -DROP TABLE IF EXISTS `#__nodes_terms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__nodes_terms` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `node_id` int(20) NOT NULL DEFAULT '0', - `term_id` int(20) NOT NULL DEFAULT '0', - `field_id` int(11) NOT NULL DEFAULT '0' COMMENT 'field instance''s ID which creates this tag assoc.', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__nodes_terms` --- - -LOCK TABLES `#__nodes_terms` WRITE; -/*!40000 ALTER TABLE `#__nodes_terms` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__nodes_terms` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/roles.sql b/app/Config/Schema/tables/roles.sql deleted file mode 100644 index 41f6047c..00000000 --- a/app/Config/Schema/tables/roles.sql +++ /dev/null @@ -1,52 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__roles` --- - -DROP TABLE IF EXISTS `#__roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__roles` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - `ordering` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__roles` --- - -LOCK TABLES `#__roles` WRITE; -/*!40000 ALTER TABLE `#__roles` DISABLE KEYS */; -INSERT INTO `#__roles` VALUES (1,'administrator',1),(2,'authenticated user',2),(3,'anonymous user',3); -/*!40000 ALTER TABLE `#__roles` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/terms.sql b/app/Config/Schema/tables/terms.sql deleted file mode 100644 index dbec109b..00000000 --- a/app/Config/Schema/tables/terms.sql +++ /dev/null @@ -1,59 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__terms` --- - -DROP TABLE IF EXISTS `#__terms`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__terms` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `vocabulary_id` int(11) NOT NULL, - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `description` text COLLATE utf8_unicode_ci, - `modified` int(11) NOT NULL, - `created` int(11) NOT NULL, - `parent_id` int(11) DEFAULT '0', - `lft` int(11) NOT NULL, - `rght` int(11) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `slug` (`slug`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__terms` --- - -LOCK TABLES `#__terms` WRITE; -/*!40000 ALTER TABLE `#__terms` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__terms` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/translations.sql b/app/Config/Schema/tables/translations.sql deleted file mode 100644 index b567da81..00000000 --- a/app/Config/Schema/tables/translations.sql +++ /dev/null @@ -1,54 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__translations` --- - -DROP TABLE IF EXISTS `#__translations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__translations` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `original` text COLLATE utf8_unicode_ci NOT NULL, - `created` int(11) NOT NULL, - `created_by` int(11) NOT NULL, - `modified` int(11) NOT NULL, - `modified_by` int(11) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__translations` --- - -LOCK TABLES `#__translations` WRITE; -/*!40000 ALTER TABLE `#__translations` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__translations` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/users.sql b/app/Config/Schema/tables/users.sql deleted file mode 100644 index 17ac6a02..00000000 --- a/app/Config/Schema/tables/users.sql +++ /dev/null @@ -1,57 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__users` --- - -DROP TABLE IF EXISTS `#__users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(60) COLLATE utf8_unicode_ci NOT NULL, - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `public_email` tinyint(1) NOT NULL DEFAULT '0', - `avatar` tinytext COLLATE utf8_unicode_ci COMMENT 'full url to avatar image file', - `language` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL, - `timezone` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, - `key` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `status` tinyint(4) NOT NULL DEFAULT '0', - `created` int(11) NOT NULL, - `modified` int(11) NOT NULL, - `last_login` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__users` --- - -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-30 1:37:16 diff --git a/app/Config/Schema/tables/users_roles.sql b/app/Config/Schema/tables/users_roles.sql deleted file mode 100644 index 05ede736..00000000 --- a/app/Config/Schema/tables/users_roles.sql +++ /dev/null @@ -1,52 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__users_roles` --- - -DROP TABLE IF EXISTS `#__users_roles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__users_roles` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `role_id` int(11) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='User HABTM Role'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__users_roles` --- - -LOCK TABLES `#__users_roles` WRITE; -/*!40000 ALTER TABLE `#__users_roles` DISABLE KEYS */; -INSERT INTO `#__users_roles` VALUES (8,1,1); -/*!40000 ALTER TABLE `#__users_roles` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/Schema/tables/variables.sql b/app/Config/Schema/tables/variables.sql deleted file mode 100644 index 48d3180b..00000000 --- a/app/Config/Schema/tables/variables.sql +++ /dev/null @@ -1,51 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__variables` --- - -DROP TABLE IF EXISTS `#__variables`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__variables` ( - `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, - `value` text COLLATE utf8_unicode_ci, - UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__variables` --- - -LOCK TABLES `#__variables` WRITE; -/*!40000 ALTER TABLE `#__variables` DISABLE KEYS */; -INSERT INTO `#__variables` VALUES ('admin_theme','s:12:\"AdminDefault\";'),('date_default_timezone','s:13:\"Europe/Madrid\";'),('default_language','s:3:\"eng\";'),('default_nodes_main','s:1:\"8\";'),('failed_login_limit','i:5;'),('rows_per_page','i:10;'),('site_description','a:0:{}'),('site_frontpage','a:0:{}'),('site_logo','s:8:\"logo.gif\";'),('site_mail','s:24:\"no-reply@your-domain.com\";'),('site_name','s:17:\"My QuickApps Site\";'),('site_online','s:1:\"1\";'),('site_slogan','s:142:\"Vivamus id feugiat ligula. Nulla facilisi. Integer lacus justo, elementum eget consequat a, molestie nec sapien. Quisque tincidunt, nunc vitae\";'),('site_theme','s:7:\"Default\";'),('user_default_avatar','s:25:\"/img/anonymous_avatar.jpg\";'),('user_mail_activation_body','s:246:\"[user_name],\r\n\r\nYour account at [site_name] has been activated.\r\n\r\nYou may now log in by clicking this link or copying and pasting it into your browser:\r\n\r\n[site_login_url]\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_activation_notify','s:1:\"1\";'),('user_mail_activation_subject','s:57:\"Account details for [user_name] at [site_name] (approved)\";'),('user_mail_blocked_body','s:85:\"[user_name],\r\n\r\nYour account on [site_name] has been blocked.\r\n\r\n-- [site_name] team\";'),('user_mail_blocked_notify','s:1:\"1\";'),('user_mail_blocked_subject','s:56:\"Account details for [user_name] at [site_name] (blocked)\";'),('user_mail_canceled_body','s:86:\"[user_name],\r\n\r\nYour account on [site_name] has been canceled.\r\n\r\n-- [site_name] team\";'),('user_mail_canceled_notify','s:1:\"1\";'),('user_mail_canceled_subject','s:57:\"Account details for [user_name] at [site_name] (canceled)\";'),('user_mail_password_recovery_body','s:273:\"[user_name],\r\n\r\nA request to reset the password for your account has been made at [site_name].\r\nYou may now log in by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nAfter log in you can reset your password.\r\n\r\n-- [site_name] team\";'),('user_mail_password_recovery_subject','s:60:\"Replacement login information for [user_name] at [site_name]\";'),('user_mail_welcome_body','s:301:\"[user_name],\r\n\r\nThank you for registering at [site_name]. You may now activate your account by clicking this link or copying and pasting it to your browser:\r\n\r\n[user_activation_url]\r\n\r\nThis link can only be used once to log in.\r\n\r\nusername: [user_name]\r\npassword: Your password\r\n\r\n-- [site_name] team\";'),('user_mail_welcome_subject','s:46:\"Account details for [user_name] at [site_name]\";'); -/*!40000 ALTER TABLE `#__variables` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-28 14:36:51 diff --git a/app/Config/Schema/tables/vocabularies.sql b/app/Config/Schema/tables/vocabularies.sql deleted file mode 100644 index dc9db6bc..00000000 --- a/app/Config/Schema/tables/vocabularies.sql +++ /dev/null @@ -1,57 +0,0 @@ --- MySQL dump 10.13 Distrib 5.5.8, for Win32 (x86) --- --- Host: localhost Database: quickapps --- ------------------------------------------------------ --- Server version 5.5.8 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `#__vocabularies` --- - -DROP TABLE IF EXISTS `#__vocabularies`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `#__vocabularies` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `description` text COLLATE utf8_unicode_ci, - `required` tinyint(1) NOT NULL DEFAULT '0', - `ordering` int(11) DEFAULT '0', - `modified` int(11) NOT NULL, - `created` int(11) NOT NULL, - PRIMARY KEY (`id`), - KEY `slug` (`slug`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `#__vocabularies` --- - -LOCK TABLES `#__vocabularies` WRITE; -/*!40000 ALTER TABLE `#__vocabularies` DISABLE KEYS */; -/*!40000 ALTER TABLE `#__vocabularies` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2011-08-26 11:37:46 diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php deleted file mode 100644 index 00952f46..00000000 --- a/app/Config/bootstrap.php +++ /dev/null @@ -1,193 +0,0 @@ - 'File')); - -/** - * The settings below can be used to set additional paths to models, views and controllers. - * - * App::build(array( - * 'plugins' => array('/full/path/to/plugins/', '/next/full/path/to/plugins/'), - * 'models' => array('/full/path/to/models/', '/next/full/path/to/models/'), - * 'views' => array('/full/path/to/views/', '/next/full/path/to/views/'), - * 'controllers' => array('/full/path/to/controllers/', '/next/full/path/to/controllers/'), - * 'datasources' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'), - * 'behaviors' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'), - * 'components' => array('/full/path/to/components/', '/next/full/path/to/components/'), - * 'helpers' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'), - * 'vendors' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'), - * 'shells' => array('/full/path/to/shells/', '/next/full/path/to/shells/'), - * 'locales' => array('/full/path/to/locale/', '/next/full/path/to/locale/') - * )); - * - */ - -/** - * As of 1.3, additional rules for the inflector are added below - * - * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); - * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); - * - */ - - App::uses('Spyc', 'vendors'); - App::uses('Folder', 'Utility'); - $__searchPath = array(); - -/** - * Load themes as plugin - */ - $folder = new Folder; - $folder->path = APP . 'View' . DS . 'Themed' . DS; - - $__themes = $folder->read(); - $__themes = $__themes[0]; - - foreach ($__themes as $__tname) { - $__searchPath[] = APP . 'View' . DS . 'Themed' . DS . $__tname . DS . 'app' . DS; - } - - $__searchPath[] = ROOT . DS . 'Modules' . DS; - - App::build(array('plugins' => $__searchPath)); - - $plugins = App::objects('plugins', null, false); - - foreach ($plugins as $plugin) { - CakePlugin::load($plugin, array('bootstrap' => true, 'routes' => true)); - - $ppath = CakePlugin::path($plugin); - $ppath = str_replace(DS . $plugin . DS, DS . Inflector::underscore($plugin) . DS, $ppath); - - if (file_exists($ppath . 'Fields' . DS)) { - App::build(array('plugins' => array($ppath . 'Fields' . DS))); - } - } - - $plugins = App::objects('plugins', null, false); - - foreach($plugins as $plugin) { - if (!CakePlugin::loaded($plugin)) { - CakePlugin::load($plugin, array('bootstrap' => true, 'routes' => true) ); - } - } - - unset($__searchPath, $__themes, $__tname, $folder, $plugins, $plugin); - -/** - * Return only the methods for the object you indicate. It will strip out the inherited methods - * web: http://php.net/manual/es/function.get-class-methods.php - * author: onesimus at cox dot net - * date: 19-Jun-2004 09:32 - */ - function get_this_class_methods($class){ - $array1 = get_class_methods($class); - - if ($parent_class = get_parent_class($class)) { - $array2 = get_class_methods($parent_class); - $array3 = array_diff($array1, $array2); - } else { - $array3 = $array1; - } - - return($array3); - } - -/** - * Translation function, domain search order: current plugin, default (global) - * - * @param string $singular String to translate - * @return string the translated string - */ - function __t($singular, $args = null) { - if (!$singular) { - return; - } - - App::uses('I18n', 'I18n'); - $route = Router::getParams(); - $translated = I18n::translate($singular, null, $route['plugin']); # look in plugin - - if ($translated === $singular) { # look in default - $translated = I18n::translate($singular, null, 'default'); - } - - if ($translated === $singular) { # llok in transtalion db-cache - $cache = Cache::read(md5($singular) . '_' . Configure::read('Config.language'), 'i18n'); - $translated = $cache ? $cache: $singular; - } - - if ($args === null) { - return $translated; - } elseif (!is_array($args)) { - $args = array_slice(func_get_args(), 1); - } - - return vsprintf($translated, $args); - } - -/** - * Create Unique Arrays using an md5 hash - * - * @param array $array - * @return array - */ - function arrayUnique($array, $preserveKeys = false) { - $arrayRewrite = array(); - $arrayHashes = array(); - - foreach ($array as $key => $item) { - $hash = md5(serialize($item)); - - if (!isset($arrayHashes[$hash])) { - $arrayHashes[$hash] = $hash; - - if ($preserveKeys) { - $arrayRewrite[$key] = $item; - } else { - $arrayRewrite[] = $item; - } - } - } - - return $arrayRewrite; - } - -/** - * replace the first ocurrence only - * - * @param string $str_pattern what to find for - * @param string $str_replacement the replacement for $str_pattern - * @param string $string the original to find and replace - * @return string - */ - function str_replace_once($str_pattern, $str_replacement, $string) { - if (strpos($string, $str_pattern) !== false) { - $occurrence = strpos($string, $str_pattern); - return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); - } - return $string; - } \ No newline at end of file diff --git a/app/Config/core.php b/app/Config/core.php deleted file mode 100644 index 7dcb9e62..00000000 --- a/app/Config/core.php +++ /dev/null @@ -1,327 +0,0 @@ - 0 - * and log errors with CakeLog when debug = 0. - * - * Options: - * - * - `handler` - callback - The callback to handle errors. You can set this to any callback type, - * including anonymous functions. - * - `level` - int - The level of errors you are interested in capturing. - * - `trace` - boolean - Include stack traces for errors in log files. - * - * @see ErrorHandler for more information on error handling and configuration. - */ - Configure::write('Error', array( - 'handler' => 'ErrorHandler::handleError', - 'level' => E_ALL & ~E_DEPRECATED, - 'trace' => true - )); - -/** - * Configure the Exception handler used for uncaught exceptions. By default, - * ErrorHandler::handleException() is used. It will display a HTML page for the exception, and - * while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0, - * framework errors will be coerced into generic HTTP errors. - * - * Options: - * - * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type, - * including anonymous functions. - * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you - * should place the file for that class in app/Error. This class needs to implement a render method. - * - `log` - boolean - Should Exceptions be logged? - * - * @see ErrorHandler for more information on exception handling and configuration. - */ - Configure::write('Exception', array( - 'handler' => 'ErrorHandler::handleException', - 'renderer' => 'ExceptionRenderer', - 'log' => true - )); - -/** - * Application wide charset encoding - */ - Configure::write('App.encoding', 'UTF-8'); - -/** - * To configure CakePHP *not* to use mod_rewrite and to - * use CakePHP pretty URLs, remove these .htaccess - * files: - * - * /.htaccess - * /app/.htaccess - * /app/webroot/.htaccess - * - * And uncomment the App.baseUrl below: - */ - //Configure::write('App.baseUrl', env('SCRIPT_NAME')); - -/** - * Uncomment the define below to use CakePHP prefix routes. - * - * The value of the define determines the names of the routes - * and their associated controller actions: - * - * Set to an array of prefixes you want to use in your application. Use for - * admin or other prefixed routes. - * - * Routing.prefixes = array('admin', 'manager'); - * - * Enables: - * `admin_index()` and `/admin/controller/index` - * `manager_index()` and `/manager/controller/index` - * - */ - Configure::write('Routing.prefixes', array('admin')); - -/** - * Turn off all caching application-wide. - * - */ - //Configure::write('Cache.disable', true); - -/** - * Enable cache checking. - * - * If set to true, for view caching you must still use the controller - * public $cacheAction inside your controllers to define caching settings. - * You can either set it controller-wide by setting public $cacheAction = true, - * or in each action using $this->cacheAction = true. - * - */ - //Configure::write('Cache.check', true); - -/** - * Defines the default error type when using the log() function. Used for - * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG. - */ - define('LOG_ERROR', 2); - -/** - * Session configuration. - * - * Contains an array of settings to use for session configuration. The defaults key is - * used to define a default preset to use for sessions, any settings declared here will override - * the settings of the default config. - * - * ## Options - * - * - `Session.name` - The name of the cookie to use. Defaults to 'CAKEPHP' - * - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP - * - `Session.cookieTimeout` - The number of minutes you want session cookies to live for. - * - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the - * value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX - * - `Session.defaults` - The default configuration set to use as a basis for your session. - * There are four builtins: php, cake, cache, database. - * - `Session.handler` - Can be used to enable a custom session handler. Expects an array of of callables, - * that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler` - * to the ini array. - * - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and - * sessionids that change frequently. See CakeSession::$requestCountdown. - * - `Session.ini` - An associative array of additional ini values to set. - * - * The built in defaults are: - * - * - 'php' -Uses settings defined in your php.ini. - * - 'cake' - Saves session files in CakePHP's /tmp directory. - * - 'database' - Uses CakePHP's database sessions. - * - 'cache' - Use the Cache class to save sessions. - * - * To define a custom session handler, save it at /app/Model/Datasource/Session/.php. - * Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to - * - * To use database sessions, run the app/Config/Schema/sessions.php schema using - * the cake shell command: cake schema create Sessions - * - */ - Configure::write('Session', array( - 'defaults' => 'php' - )); - -/** - * The level of CakePHP security. - */ - Configure::write('Security.level', 'medium'); - -/** - * A random string used in security hashing methods. - */ - Configure::write('Security.salt', 'alknLsnÑQUCMdnweuNsd83P390dm'); - -/** - * A random numeric string (digits only) used to encrypt/decrypt strings. - */ - Configure::write('Security.cipherSeed', '8701467490498213157498310'); - -/** - * Apply timestamps with the last modified time to static assets (js, css, images). - * Will append a querystring parameter containing the time the file was modified. This is - * useful for invalidating browser caches. - * - * Set to `true` to apply timestamps, when debug = 0, or set to 'force' to always enable - * timestamping. - */ - //Configure::write('Asset.timestamp', true); -/** - * Compress CSS output by removing comments, whitespace, repeating tags, etc. - * This requires a/var/cache directory to be writable by the web server for caching. - * and /vendors/csspp/csspp.php - * - * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css(). - */ - //Configure::write('Asset.filter.css', 'css.php'); - -/** - * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the - * output, and setting the config below to the name of the script. - * - * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link(). - */ - //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php'); - -/** - * The classname and database used in CakePHP's - * access control lists. - */ - Configure::write('Acl.classname', 'DbAcl'); - Configure::write('Acl.database', 'default'); - -/** - * If you are on PHP 5.3 uncomment this line and correct your server timezone - * to fix the date & time related errors. - */ - //date_default_timezone_set('UTC'); - -/** - * - * Cache Engine Configuration - * Default settings provided below - * - * File storage engine. - * - * Cache::config('default', array( - * 'engine' => 'File', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path - * 'prefix' => 'cake_', //[optional] prefix every cache file with this string - * 'lock' => false, //[optional] use file locking - * 'serialize' => true, [optional] - * )); - * - * - * APC (http://pecl.php.net/package/APC) - * - * Cache::config('default', array( - * 'engine' => 'Apc', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * )); - * - * Xcache (http://xcache.lighttpd.net/) - * - * Cache::config('default', array( - * 'engine' => 'Xcache', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * 'user' => 'user', //user from xcache.admin.user settings - * 'password' => 'password', //plaintext password (xcache.admin.pass) - * )); - * - * - * Memcache (http://www.danga.com/memcached/) - * - * Cache::config('default', array( - * 'engine' => 'Memcache', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * 'servers' => array( - * '127.0.0.1:11211' // localhost, default port 11211 - * ), //[optional] - * 'persistent' => true, // [optional] set this to false for non-persistent connections - * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) - * 'persistent' => true, // [optional] set this to false for non-persistent connections - * )); - * - */ - -/** - * Pick the caching engine to use. If APC is enabled use it. - * If running via cli - apc is disabled by default. ensure it's available and enabled in this case - * - */ -$engine = 'File'; -if (extension_loaded('apc') && (php_sapi_name() !== 'cli' || ini_get('apc.enable_cli'))) { - $engine = 'Apc'; -} - -// In development mode, caches should expire quickly. -$duration = '+999 days'; -if (Configure::read('debug') >= 1) { - $duration = '+10 seconds'; -} - -/** - * Configure the cache used for general framework caching. Path information, - * object listings, and translation cache files are stored with this configuration. - */ -Cache::config('_cake_core_', array( - 'engine' => $engine, - 'prefix' => 'cake_core_', - 'path' => CACHE . 'persistent' . DS, - 'serialize' => ($engine === 'File'), - 'duration' => $duration -)); - -/** - * Configure the cache for model, and datasource caches. This cache configuration - * is used to store schema descriptions, and table listings in connections. - */ -Cache::config('_cake_model_', array( - 'engine' => $engine, - 'prefix' => 'cake_model_', - 'path' => CACHE . 'models' . DS, - 'serialize' => ($engine === 'File'), - 'duration' => $duration -)); diff --git a/app/Config/database.php.install b/app/Config/database.php.install deleted file mode 100644 index aecfe3cb..00000000 --- a/app/Config/database.php.install +++ /dev/null @@ -1,71 +0,0 @@ - The name of a supported driver; valid options are as follows: - * Database/Mysql - MySQL 4 & 5, - * Database/Sqlite - SQLite (PHP5 only), - * Database/Postgres - PostgreSQL 7 and higher, - * Database/Sqlserver - Microsoft SQL Server 2005 and higher, - * Database/Oracle - Oracle 8 and higher - * - * You can add custom database drivers (or override existing drivers) by adding the - * appropriate file to app/Model/Datasource/Database. Drivers should be named 'MyDriver.php', - * - * - * persistent => true / false - * Determines whether or not the database should use a persistent connection - * - * host => - * the host you connect to the database. To add a socket or port number, use 'port' => # - * - * prefix => - * Uses the given prefix for all the tables in this database. This setting can be overridden - * on a per-table basis with the Model::$tablePrefix property. - * - * schema => - * For Postgres specifies which schema you would like to use the tables in. Postgres defaults to 'public'. - * - * encoding => - * For MySQL, Postgres specifies the character encoding to use when connecting to the - * database. Uses database default not specified. - * - */ -class DATABASE_CONFIG { - - public $default = array( - 'datasource' => 'Database/Mysql', - 'persistent' => {db_persistent}, - 'host' => '{db_host}', - 'login' => '{db_login}', - 'password' => '{db_password}', - 'database' => '{db_database}', - 'prefix' => '{db_prefix}', - 'encoding' => 'UTF8' - ); -} diff --git a/app/Config/email.php.default b/app/Config/email.php.default deleted file mode 100644 index f55496ae..00000000 --- a/app/Config/email.php.default +++ /dev/null @@ -1,88 +0,0 @@ - The name of a supported transport; valid options are as follows: - * Mail - Send using PHP mail function - * Smtp - Send using SMTP - * - * You can add custom transports (or override existing transports) by adding the - * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', - * where 'Your' is the name of the transport. - * - * from => - * The origin email. See CakeEmail::from() about the valid values - * - */ -class EmailConfig { - - public $default = array( - 'transport' => 'Mail', - 'from' => 'you@localhost' - ); - - public $smtp = array( - 'transport' => 'Smtp', - 'from' => array('My Site', 'site@localhost'), - 'host' => 'localhost', - 'port' => 25, - 'timeout' => 30, - 'username' => 'user', - 'password' => 'secret', - 'client' => null - ); - - public $fast = array( - 'from' => 'you@localhost', - 'sender' => null, - 'to' => null, - 'cc' => null, - 'bcc' => null, - 'replyTo' => null, - 'readReceipt' => null, - 'returnPath' => null, - 'messageId' => true, - 'subject' => null, - 'message' => null, - 'headers' => null, - 'viewRender' => null, - 'template' => false, - 'layout' => false, - 'viewVars' => null, - 'attachments' => null, - 'emailFormat' => null, - 'transport' => 'Smtp', - 'host' => 'localhost', - 'port' => 25, - 'timeout' => 30, - 'username' => 'user', - 'password' => 'secret', - 'client' => null - ); - -} diff --git a/app/Config/routes.php b/app/Config/routes.php deleted file mode 100644 index a4fbb328..00000000 --- a/app/Config/routes.php +++ /dev/null @@ -1,53 +0,0 @@ - 'install')); - } else { - Router::connect('/', array('plugin' => 'node', 'controller' => 'node', 'action' => 'index')); - Router::connect('/admin', array('plugin' => 'system', 'controller' => 'system', 'action' => 'index', 'admin' => true )); - } - -/** - * Load all plugin routes. See the CakePlugin documentation on - * how to customize the loading of plugin routes. - */ - CakePlugin::routes(); - -/** - * Load the CakePHP default routes. Remove this if you do not want to use - * the built-in default routes. - */ - require CAKE . 'Config' . DS . 'routes.php'; \ No newline at end of file diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php deleted file mode 100644 index 7fcd11fd..00000000 --- a/app/Controller/AppController.php +++ /dev/null @@ -1,294 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class AppController extends Controller { - public $view = 'Theme'; - public $theme = 'default'; - - public $Layout = array( - 'feed' => null, # url to rss feed - 'blocks' => array(), - 'node' => array(), - 'viewMode' => '', # full, list - 'header' => array(), # extra code for header - 'footer' => array(), # extra code for - 'stylesheets' => array( - 'all' => array(), - 'braille' => array(), - 'embossed' => array(), - 'handheld' => array(), - 'print' => array(), - 'projection' => array(), - 'screen' => array(), - 'speech' => array(), - 'tty' => array(), - 'tv' => array(), - 'embed' => array() - ), - 'javascripts' => array( - 'embed' => array(), - 'file' => array('jquery.js', 'quickapps.js') - ), - 'meta' => array() # meta tags for layout - ); - - public $helpers = array( - 'Layout', - 'Form' => array('className' => 'QaForm'), - 'Html' => array('className' => 'QaHtml'), - 'Session', - 'Cache', - 'Js', - 'Time' - ); - - public $uses = array( - 'System.Variable', - 'System.Module', - 'Menu.MenuLink', - 'Locale.Language' - ); - - public $components = array( - 'Session', - 'Cookie', - 'RequestHandler', - 'Hook', - 'Acl', - 'Auth', - 'Quickapps' - ); - - public function __construct($request = null, $response = null) { - $this->__preloadHooks(); - parent::__construct($request, $response); - } - - public function beforeRender() { - if ($this->Layout['feed']) { - $this->Layout['meta']['link'] = $this->Layout['feed']; - } - - $this->set('Layout', $this->Layout); - - if ($this->name == 'CakeError') { - $this->beforeFilter(); - $this->layout = 'error'; - } - - return true; - } - - public function isAuthorized($user) { - $this->Quickapps->accessCheck(); - - $isAllowed = ( - $this->Auth->allowedActions == array('*') || - in_array($this->request->params['action'], $this->Auth->allowedActions) - ); - - return $isAllowed; - } - -/** - * shortcut for $this->set(`title_for_layout`...) - * - * @param string $str layout title - * @return void - */ - public function title($str) { - return $this->Quickapps->title($str); - } - -/** - * shortcut for Session setFlash - * - * @param string $msg mesagge to display - * @param string $class type of message: error, success, alert, bubble - * @return void - */ - public function flashMsg($msg, $class = 'success') { - return $this->Quickapps->flashMsg($msg, $class); - } - -/** - * Insert custom block in stack - * - * @param array $data formatted block array - * @param string $region theme region where to push - * @return boolean - */ - public function blockPush($block = array(), $region = null, $show_on = true) { - return $this->Quickapps->blockPush($block, $region, $show_on); - } - -/** - * Wrapper method to Hook::hook_defined - * - * @param string $hook Name of the event - * @return bool - */ - public function hook_defined($hook) { - return $this->Hook->hook_defined($hook); - } - -/** - * Overwrite default options for Hook dispatcher. - * Useful when calling a hook with non-parameter and custom options. - * - * Watch out!: Hook dispatcher automatic reset its default options to - * the original ones after `hook()` is invoked. - * Means that if you need to call more than one hook (consecutive) with no parameters and - * same options ** you must call `setHookOptions()` after each hook() ** - * - * ### Usage - * For example in any controller action: - * {{{ - * $this->setHookOptions(array('collectReturn' => false)); - * $response = $this->hook('collect_hook_with_no_parameters'); - * - * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); - * $response2 = $this->hook('no_collect_and_breakOn_hook_with_no_parameters'); - * }}} - * - * @param array $options Array of options to overwrite - * @return void - */ - public function setHookOptions($options) { - $this->Hook->setHookOptions($options); - } - -/** - * Wrapper method to Hook::__dispatchEvent - * - * @param string $hook Name of the event - * @param mix $data Any data to attach - * @param bool $raw_return false means return asociative data, true will return a listed array - * @return mixed FALSE -or- result array - */ - public function hook($hook, &$data = array(), $options = array()) { - return $this->Hook->hook($hook, $data, $options); - } - -/** - * Set crumb from url parse or add url to the links list - * - * @param mixed $url if is array then will push de formated array to the crumbs list - * else will set base crum from string parsing - * @return void - */ - public function setCrumb($url = false) { - return $this->Quickapps->setCrumb($url); - } - -/** - * Load and attach hooks to AppController. (Hooks can be Components, Helpers, Behaviours) - * - Preload helpers hooks - * - Preload behaviors hooks - * - Preload components hooks - * - * @return void - */ - private function __preloadHooks() { - $paths = $c = $h = $b = array(); - - // load current theme hooks only - $_variable = Cache::read('Variable'); - $_modules = Cache::read('Modules'); - $_themeType = Router::getParam('admin') ? 'admin_theme' : 'site_theme'; - - if (!$_variable) { - $this->loadModel('System.Variable'); - - $_variable = $this->Variable->find('first', array('conditions' => array('Variable.name' => $_themeType))); - $_variable[$_themeType] = $_variable['Variable']['value']; - - ClassRegistry::flush(); - unset($this->Variable); - } - - if (!$_modules) { - $this->loadModel('System.Module'); - - foreach ($this->Module->find('all', array('fields' => array('name'), 'conditions' => array('Module.status' => 1))) as $m) { - $_modules[$m['Module']['name']] = array(); - } - - ClassRegistry::flush(); - unset($this->Module); - } - - $_modules = array_keys($_modules); - $themeToUse = $_variable[$_themeType]; - $plugins = App::objects('plugin', null, false); - - foreach ($plugins as $plugin) { - $ppath = CakePlugin::path($plugin); - $modulesCache = Cache::read('Modules'); - $_plugin = Inflector::underscore($plugin); - - # inactive module, except fields that are nor registered as plugin en DB - if (!in_array($_plugin, $_modules) && strpos($ppath, DS . 'Fields' . DS) === false) { - continue; - } - - if ((isset($modulesCache[$_plugin]['status']) && $modulesCache[$_plugin]['status'] == 0) || - (strpos($ppath, DS . 'View' . DS . 'Themed') !== false && strpos($ppath, 'Themed' . DS . $themeToUse . DS . 'app') === false) - ) { - continue; # Important: skip no active themes - } - - $paths["{$plugin}_components"] = $ppath . 'Controller' . DS . 'Component' . DS; - $paths["{$plugin}_behaviors"] = $ppath . 'Model' . DS . 'Behavior' . DS; - $paths["{$plugin}_helpers"] = $ppath . 'View' . DS . 'Helper' . DS; - } - - $paths = array_merge( - array( - APP . 'Controller' . DS . 'Components' . DS, # core components - APP . 'View' . DS . 'Helper' . DS, # core helpers - APP . 'Model' . DS . 'Behavior' . DS # core behaviors - ), - (array)$paths - ); - - $folder = new Folder; - - foreach ($paths as $key => $path) { - $folder->path = $path; - $files = $folder->find('(.*)Hook(Component|Behavior|Helper)\.php'); - $plugin = is_string($key) ? explode('_', $key) : false; - $plugin = is_array($plugin) ? $plugin[0] : $plugin; - - foreach ($files as $file) { - $prefix = ($plugin) ? Inflector::camelize($plugin) . '.' : ''; - $hook = $prefix . Inflector::camelize(str_replace(array('.php'), '', basename($file))); - $hook = str_replace(array('Component', 'Behavior', 'Helper'),'', $hook); - - if (strpos($path, 'Helper')) { - $h[] = $hook; - $this->helpers[] = $hook; - } elseif (strpos($path, 'Behavior')) { - $b[] = $hook; - } else { - $c[] = $hook; - $this->components[] = $hook; - } - } - } - - $h[] = 'CustomHooks'; # merge custom hooktags helper - - Configure::write('Hook.components', $c); - Configure::write('Hook.behaviors', $b); - Configure::write('Hook.helpers', $h); - } -} \ No newline at end of file diff --git a/app/Controller/Component/HookComponent.php b/app/Controller/Component/HookComponent.php deleted file mode 100644 index 4f1aa65e..00000000 --- a/app/Controller/Component/HookComponent.php +++ /dev/null @@ -1,332 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class HookComponent extends Component { - public $Controller; - public $listeners = array(); - public $events = array(); - public $eventMap = array(); - public $Options = array( - 'break' => false, - 'breakOn' => false, - 'collectReturn' => false - ); - private $__Options = array( - 'break' => false, - 'breakOn' => false, - 'collectReturn' => false - ); - - public function startup() { } - public function beforeRender() { } - public function shutdown() { } - public function beforeRedirect() {} - -/** - * Called before the Controller::beforeFilter(). - * - * @param object $controller Controller with components to initialize - * @return void - */ - public function initialize(&$Controller) { - $this->Controller =& $Controller; - $eventMap = array(); - - foreach (Configure::read('Hook.components') as $component) { - $component = strpos($component, '.') !== false ? substr($component, strpos($component, '.')+1) : $component; - - if (strpos($component, 'Hook')) { - $methods = array(); - $_methods = get_this_class_methods($this->Controller->{$component}); - - foreach ($_methods as $method) { - $methods[] = $method; - $eventMap[$method] = (string)$component; - } - - $this->listeners[$component] = $methods; - $this->events = array_merge($this->events, $methods); - $this->eventMap = array_merge($this->eventMap, $eventMap); - } - } - - $this->events = array_unique($this->events); - - return true; - } - -/** - * Parse string for special placeholders - * placeholder example: [hook_function param1=text param=2 param3=0 ... /] - * [other_hook_function]only content & no params[/other_hook_function] - * - * @return string HTML - */ - public function hookTags($text) { - $text = $this->specialTags($text); - $tags = implode('|', $this->events); - - return preg_replace_callback('/(.?)\[(' . $tags . ')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s', array($this, '__doHookTag'), $text); - } - -/** - * Replace some core useful tags: - * `[date=FORMAT]` Return current date(FORMAT). - * `[language.OPTION]` Current language option (code, name, native, direction). - * `[language]` Shortcut to [language.code] which return current language code. - * `[url]YourURL[/url]` or `[url=YourURL]` Formatted url. - * `[url=LINK]LABEL[/url]` Returns link tag LABEL - * `[t=stringToTranslate]` or `[t]stringToTranslate[/t]` text translation: __t(stringToTranslate) - * `[t=domain@@stringToTranslate]` Translation by domain __d(domain, stringToTranslate) - * - * @param string $text original text where to replace tags - * @return string - */ - public function specialTags($text) { - // [locale] - $text = str_replace('[language]', Configure::read('Variable.language.code'), $text); - - //[locale.OPTION] - preg_match_all('/\[language.(.+)\]/iUs', $text, $localeMatches); - foreach ($localeMatches[1] as $attr) { - $text = str_replace("[language.{$attr}]", Configure::read('Variable.language.' .$attr), $text); - } - - //[url]URL[/url] - preg_match_all('/\[url\](.+)\[\/url\]/iUs', $text, $urlMatches); - foreach ($urlMatches[1] as $url) { - $text = str_replace("[url]{$url}[/url]", $this->_View->Html->url($url, true), $text); - } - - //[url=URL] - preg_match_all('/\[url\=(.+)\]/iUs', $text, $urlMatches); - foreach ($urlMatches[1] as $url) { - $text = str_replace("[url={$url}]", $this->_View->Html->url($url, true), $text ); - } - - //[t=text to translate] - preg_match_all('/\[t\=(.+)\]/iUs', $text, $tMatches); - foreach ($tMatches[1] as $string) { - $text = str_replace("[t={$string}]", __t($string), $text); - } - - //[t]text to translate[/t] - preg_match_all('/\[t\](.+)\[\/t\]/iUs', $text, $tMatches); - foreach ($tMatches[1] as $string) { - $text = str_replace("[t]{$string}[/t]", __t($string), $text); - } - - //[t=domain@@text to translate] - preg_match_all('/\[t\=(.+)\@\@(.+)\]/iUs', $text, $dMatches); - foreach ($dMatches[1] as $key => $domain) { - $text = str_replace("[d={$domain}@@{$dMatches[2][$key]}]", __d($domain, $dMatches[2][$key]), $text ); - } - - //[date=FORMAT@@TIME_STAMP] - preg_match_all('/\[date\=(.+)\@\@(.+)\]/iUs', $text, $dateMatches); - foreach ($dateMatches[1] as $key => $format) { - $stamp = $dateMatches[2][$key]; - $replace = is_numeric($stamp) ? date($format, $stamp) : date($format, strtotime($stamp)); - $text = str_replace("[date={$format}@@{$stamp}]", $replace, $text); - } - - //[date=FORMAT] - preg_match_all('/\[date\=(.+)\]/iUs', $text, $dateMatches); - foreach ($dateMatches[1] as $format) { - $text = str_replace("[date={$format}]", date($format), $text); - } - - # pass text to modules so they can apply their own special tags - $this->hook('specialTags_alter', $text); - - return $text; - } - -/** - * Chech if hook exists - * - * @param string $hook Name of the hook to check - * @return boolean - */ - public function hook_defined($hook) { - return (in_array($hook, $this->events) == true); - } - -/** - * Trigger a callback method on every HookComponent. - * - * ### Options - * - * - `breakOn` Set to the value or values you want the callback propagation to stop on. - * Can either be a scalar value, or an array of values to break on. - * Defaults to `false`. - * - * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value - * will be returned. If used in combination with `collectReturn` the collected results will be returned. - * Defaults to `false`. - * - * - `collectReturn` Set to true to collect the return of each object into an array. - * This array of return values will be returned from the hook() call. Defaults to `false`. - * - * - `alter` Allows each callback gets called on to modify the parameters to the next object. - * Defaults to true. - * - * @param string $event name of the hook to call - * @param mixed $data data for the triggered callback - * @param array $option Array of options - * @return mixed Either the last result or all results if collectReturn is on. Or null in case of no response - */ - public function hook($event, &$data = array(), $options = array()) { - return $this->__dispatchEvent($event, $data, $options); - } - -/** - * Overwrite default options for Hook dispatcher. - * Useful when calling a hook with non-parameter and custom options. - * - * Watch out!: Hook dispatcher automatic reset its default options to - * the original ones after `hook()` is invoked. - * Means that if you need to call more than one hook (consecutive) with no parameters and - * same options ** you must call `setHookOptions()` after each hook() ** - * - * ### Usage - * For example in any controller action: - * {{{ - * $this->setHookOptions(array('collectReturn' => false)); - * $response = $this->hook('collect_hook_with_no_parameters'); - * - * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); - * $response2 = $this->hook('OTHER_collect_hook_with_no_parameters'); - * }}} - * - * @param array $options Array of options to overwrite - * @return void - */ - public function setHookOptions($options) { - $this->Options = Set::merge($this->Options, $options); - } - -/** - * Parse hook tags attributes - * - * @param string $text Tag string to parse - * @return Array array of attributes - */ - private function __hookTagParseAtts($text) { - $atts = array(); - $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; - $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); - - if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) { - foreach ($match as $m) { - if (!empty($m[1])) { - $atts[strtolower($m[1])] = stripcslashes($m[2]); - } elseif (!empty($m[3])) { - $atts[strtolower($m[3])] = stripcslashes($m[4]); - } elseif (!empty($m[5])) { - $atts[strtolower($m[5])] = stripcslashes($m[6]); - } elseif (isset($m[7]) and strlen($m[7])) { - $atts[] = stripcslashes($m[7]); - } elseif (isset($m[8])) { - $atts[] = stripcslashes($m[8]); - } - } - } else { - $atts = ltrim($text); - } - - return $atts; - } - -/** - * Callback function - * - * @see hookTags() - * @return mixed Hook response or false in case of no response. - */ - private function __doHookTag($m) { - // allow [[foo]] syntax for escaping a tag - if ($m[1] == '[' && $m[6] == ']') { - return substr($m[0], 1, -1); - } - - $tag = $m[2]; - $attr = $this->__hookTagParseAtts( $m[3] ); - $hook = isset($this->eventMap[$tag]) ? $this->eventMap[$tag] : false; - - if ($hook) { - $hook =& $this->Controller->{$hook}; - - if (isset( $m[5] )) { - // enclosing tag - extra parameter - return $m[1] . call_user_func(array($hook, $tag), $attr, $m[5], $tag) . $m[6]; - } else { - // self-closing tag - return $m[1] . call_user_func(array($hook, $tag), $attr, null, $tag) . $m[6]; - } - } - - return false; - } - -/** - * Dispatch Component-hooks from all the plugins and core - * - * @see HookComponent::hook() - * @return mixed Either the last result or all results if collectReturn is on. Or NULL in case of no response - */ - private function __dispatchEvent($event, &$data = array(), $options = array()) { - $options = array_merge($this->Options, (array)$options); - $collected = array(); - - if (!$this->hook_defined($event)) { - $this->__resetOptions(); - - return null; - } - - foreach ($this->listeners as $component => $methods) { - foreach ($methods as $method) { - if ($method == $event && is_callable(array($this->Controller->{$component}, $method))) { - $result = call_user_func(array($this->Controller->{$component}, $event), $data); - - if ($options['collectReturn'] === true) { - $collected[] = $result; - } - - if ($options['break'] && - ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true))) - ) { - $this->__resetOptions(); - - return $result; - } - } - } - } - - if (empty($collected) && in_array($result, array('', null), true)) { - $this->__resetOptions(); - - return null; - } - - $this->__resetOptions(); - - return $options['collectReturn'] ? $collected : $result; - } - - private function __resetOptions() { - if ($this->Options !== $this->__Options) { - $this->Options = $this->__Options; - } - } -} \ No newline at end of file diff --git a/app/Controller/Component/InstallerComponent.php b/app/Controller/Component/InstallerComponent.php deleted file mode 100644 index 45fa606e..00000000 --- a/app/Controller/Component/InstallerComponent.php +++ /dev/null @@ -1,881 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class InstallerComponent extends Component { - public $errors = array(); - public $Controller; - public $options = array( - 'name' => null, # used while deleting - 'type' => 'module', # type of package to install - 'status' => 1 # install and activate(status=1), install and do not activate (status=0) - ); - - public function startup() {} - public function beforeRender() {} - public function shutdown() {} - public function beforeRedirect() {} - - public function initialize(&$Controller) { - $this->Controller =& $Controller; - - return true; - } - -/** - * Begin instalation proccess for the indicated package. - * Expected module package estructure: - * - module_folder_name/ # $package_path - * - Config/ - * - Controller/ - * - Component/ - * - InstallComponent.php - * - Lib/ - * - Locale/ - * - Model/ - * - View/ - * - webroot/ - * - module_folder_name.yaml - * - * Expected theme package estructure: - * - CamelCaseThemeName/ # $package_path - * - Layouts/ - * - app/ - * - theme_underscore_theme_name/ # 'theme_' + {underscored theme name} - * - webroot/ - * - CamelCaseThemeName.yaml - * - thumbnail.png # 206x150px - * - * - * @param array $data form POST submit of the .app package ($this->data) - * @param array $options optional settings, see InstallerComponent::$options - * @return bool true on success or false otherwise - */ - public function install($data = false, $options = array()) { - if (!$data) { - return false; - } - - $oldMask = umask(0); - $this->options = array_merge($this->options, $options); - $ext = strtolower(strrchr($data['Package']['data']['name'], '.')); - - if ($ext !== '.app') { - $this->errors[] = __d('system', 'Invalid package extension. Got `%s`, `.app` expected', $ext); - return false; - } - - /**********/ - /* upload */ - /**********/ - App::import('Vendor', 'Upload'); - - $uploadPath = CACHE. 'installer'; - $workingDir = CACHE . 'installer' . DS . $data['Package']['data']['name'] . DS; - $Folder = new Folder; - $Upload = new Upload($data['Package']['data']); - $Upload->allowed = array('application/*'); - $Upload->file_overwrite = true; - $Upload->file_src_name_ext = 'zip'; - - $Folder->delete($workingDir); - $Upload->Process($workingDir . 'package' . DS); - - if (!$Upload->processed) { - $this->errors[] = __d('system', 'Package upload error') . "

    {$Upload->error}

    "; - return false; - } - - /*******************/ - /* unzip & install */ - /*******************/ - App::import('Vendor', 'PclZip'); - - $PclZip = new PclZip($Upload->file_dst_pathname); - - if (($v_result_list = $PclZip->extract(PCLZIP_OPT_PATH, $workingDir . 'unzip')) == 0 ) { - $this->errors[] = __d('system', 'Unzip error.') . "

    " . $PclZip->errorInfo(true) . "

    "; - - return false; - } else { - /* Package Validation */ - $Folder->path = $workingDir . 'unzip' . DS; - $folders = $Folder->read();$folders = $folders[0]; - $packagePath = isset($folders[0]) && count($folders) === 1 ? CACHE . 'installer' . DS . $data['Package']['data']['name'] . DS . 'unzip' . DS . str_replace(DS, '', $folders[0]) . DS : false; - $appName = (string)basename($packagePath); - - if (!$packagePath) { - $this->errors[] = __d('system', 'Invalid package structure after unzip'); - - return false; - } - - switch ($this->options['type']) { - case 'module': - default: - $tests = array( - 'notAlreadyInstalled' => array( - 'test' => ( - $this->Controller->Module->find('count', array('conditions' => array('Module.name' => $appName, 'Module.type' => 'module'))) === 0 && - !file_exists(ROOT . DS . 'Modules' . DS . $appName) - ), - 'header' => __d('system', 'Already Installed'), - 'msg' => __d('system', 'This module is already installed') - ), - 'protectedPrefix' => array( - 'test' => (strpos(Inflector::underscore($appName), 'theme_') === false), - 'header' => __d('system', 'Invalid prefix'), - 'msg' => __d('system', "The prefix 'theme_' is not allowed for modules.") - ), - 'Config' => array( - 'test' => file_exists($packagePath . 'Config'), - 'header' => __d('system', 'Config Folder'), - 'msg' => __d('system', 'Config folder not found') - ), - 'Controller' => array( - 'test' => file_exists($packagePath . 'Controller'), - 'header' => __d('system', 'Controller Folder'), - 'msg' => __d('system', 'Controller folder not found') - ), - 'Component' => array( - 'test' => file_exists($packagePath . 'Controller' . DS . 'Component'), - 'header' => __d('system', 'Component Folder'), - 'msg' => __d('system', 'Component folder not found') - ), - 'InstallComponent.php' => array( - 'test' => file_exists($packagePath . 'Controller' . DS . 'Component' . DS . 'InstallComponent.php'), - 'header' => __d('system', 'Installer File'), - 'msg' => __d('system', 'Installer file (InstallComponent.php) not found') - ), - 'Lib' => array( - 'test' => file_exists($packagePath . 'Lib'), - 'header' => __d('system', 'Lib Folder'), - 'msg' => __d('system', 'Lib folder not found') - ), - 'Locale' => array( - 'test' => file_exists($packagePath . 'Locale'), - 'header' => __d('system', 'Locale Folder'), - 'msg' => __d('system', 'Locale folder not found') - ), - 'Model' => array( - 'test' => file_exists($packagePath . 'Model'), - 'header' => __d('system', 'Model Folder'), - 'msg' => __d('system', 'Model folder not found') - ), - 'yaml' => array( - 'test' => file_exists($packagePath . "{$appName}.yaml"), - 'header' => __d('system', 'YAML File'), - 'msg' => __d('system', 'YAML File (%s) not found', "{$appName}.yaml") - ) - ); - break; - - case 'theme': - $tests = array( - 'notAlreadyInstalled' => array( - 'test' => ( - $this->Controller->Module->find('count', array('conditions' => array('Module.name' => 'theme_' . Inflector::underscore($appName), 'Module.type' => 'theme'))) === 0 && - !file_exists(APP . 'View' . DS . 'Themed' . DS . $appName) - ), - 'header' => __d('system', 'Already Installed'), - 'msg' => __d('system', 'This theme is already installed') - ), - 'CamelCaseName' => array( - 'test' => (Inflector::camelize($appName) == $appName), - 'header' => __d('system', 'Theme name'), - 'msg' => __d('system', 'Invalid theme name (got "%s", expected: "%s")', $appName, Inflector::camelize($appName)) - ), - 'Layouts' => array( - 'test' => file_exists($packagePath . 'Layouts'), - 'header' => __d('system', 'Layouts Folder'), - 'msg' => __d('system', '"Layouts" folder not found') - ), - 'app' => array( - 'test' => file_exists($packagePath . 'app'), - 'header' => __d('system', 'app Folder'), - 'msg' => __d('system', '"app" folder not found') - ), - 'plugin_app' => array( - 'test' => file_exists($packagePath . 'app' . DS . 'theme_' . Inflector::underscore($appName)), - 'header' => __d('system', 'Plugin app'), - 'msg' => __d('system', 'Plugin app ("%s") folder not found', 'theme_' . Inflector::underscore($appName)) - ), - 'InstallComponent.php' => array( - 'test' => file_exists($packagePath . 'app' . DS . 'theme_' . Inflector::underscore($appName). DS . 'Controller' . DS . 'Component' . DS . 'InstallComponent.php'), - 'header' => __d('system', 'Installer File'), - 'msg' => __d('system', 'Installer file (InstallComponent.php) not found') - ), - 'webroot' => array( - 'test' => file_exists($packagePath . 'webroot'), - 'header' => __d('system', 'webroot Folder'), - 'msg' => __d('system', 'webroot folder not found') - ), - 'yaml' => array( - 'test' => file_exists($packagePath . "{$appName}.yaml"), - 'header' => __d('system', 'YAML File'), - 'msg' => __d('system', 'YAML File (%s) not found', "{$appName}.yaml") - ), - 'thumbnail' => array( - 'test' => file_exists($packagePath . 'thumbnail.png'), - 'header' => __d('system', 'Theme thumbnail'), - 'msg' => __d('system', 'Thumbnail image ("%s") not found', 'thumbnail.png') - ) - ); - break; - } - - if (!$this->__process_tests($tests)) { - return false; - } - - /** YAML validations **/ - $yaml = Spyc::YAMLLoad($packagePath . "{$appName}.yaml"); - - switch ($this->options['type']) { - case 'module': - default: - $tests = array( - 'yaml' => array( - 'test' => ( - (isset($yaml['name']) && !empty($yaml['name'])) && - (isset($yaml['description']) && !empty($yaml['description'])) && - (isset($yaml['category']) && !empty($yaml['category'])) && - (isset($yaml['version']) && !empty($yaml['version'])) && - (isset($yaml['core']) && !empty($yaml['core'])) - ), - 'header' => __d('system', 'YAML Validation'), - 'msg' => __d('system', 'Module configuration file (%s) appears to be invalid.', "{$appName}.yaml") - ) - ); - break; - - case 'theme': - $tests = array( - 'yaml' => array( - 'test' => ( - (isset($yaml['info']) && !empty($yaml['info'])) && - (isset($yaml['info']['name']) && !empty($yaml['info']['name'])) && - (isset($yaml['info']['description']) && !empty($yaml['info']['description'])) && - (isset($yaml['info']['version']) && !empty($yaml['info']['version'])) && - (isset($yaml['info']['author']) && !empty($yaml['info']['author'])) && - (isset($yaml['info']['core']) && !empty($yaml['info']['core'])) && - isset($yaml['stylesheets']) && - (isset($yaml['regions']) && !empty($yaml['regions'])) && - (isset($yaml['layout']) && !empty($yaml['layout'])) - ), - 'header' => __d('system', 'YAML Validation'), - 'msg' => __d('system', 'Theme configuration file (%s) appears to be invalid.', "{$appName}.yaml") - ) - ); - break; - } - - if (!$this->__process_tests($tests)) { - $this->errors[] = __d('system', 'Invalid information file (.yaml)'); - - return false; - } - - /** - * validate dependencies and required core version - */ - switch ($this->options['type']) { - case 'module': - $core = "core ({$yaml['core']})"; - $r = $this->checkIncompatibility($this->parseDependency($core), Configure::read('Variable.qa_version')); - - if ($r !== null) { - $this->errors[] = __d('system', 'This module is incompatible with your QuickApps version.'); - - return false; - } - - if (isset($yaml['dependencies']) && $this->checkDependency($yaml)) { - $this->errors[] = __d('system', "This module depends on other modules that you do not have or doesn't meet the version required: %s", implode('
    ', $yaml['dependencies'])); - - return false; - } - break; - - case 'theme': - $core = "core ({$yaml['info']['core']})"; - $r = $this->checkIncompatibility($this->parseDependency($core), Configure::read('Variable.qa_version')); - - if ($r !== null) { - $this->errors[] = __d('system', 'This theme is incompatible with your QuickApps version.'); - - return false; - } - - if (isset($yaml['info']['dependencies']) && $this->checkDependency($yaml['info'])) { - $this->errors[] = __d('system', "This theme depends on other modules that you do not have or doesn't meet the version required: %s", implode('
    ', $yaml['info']['dependencies'])); - - return false; - } - break; - } - - /** - * validate custom fields - * Only modules are allowed to define fields. - */ - if ($this->options['type'] == 'module' && file_exists($packagePath . 'Fields')) { - $Folder = new Folder($packagePath . 'Fields'); - $fields = $Folder->read(); - $fieldErrors = false; - - if (isset($fields[0])) { - $fields = $fields[0]; - - foreach ($fields as $field) { - if (file_exists($packagePath . 'Fields' . DS . $field . DS . "{$field}.yaml")) { - $yaml = Spyc::YAMLLoad($packagePath . 'Fields' . DS . $field . DS . "{$field}.yaml"); - - if (!isset($yaml['name']) || !isset($yaml['description'])) { - $fieldErrors = true; - $this->errors[] = __d('system', 'invalid information file (.yaml). Field "%s"', $field); - } - } else { - $fieldErrors = true; - $this->errors[] = __d('system', 'Invalid field "%s". Information file (.yaml) not found.', $field); - } - } - } - - if ($fieldErrors) { - return false; - } - } - ### End of validations ### - - - /*****************/ - /**** INSTALL ****/ - /*****************/ - $installComponentPath = $this->options['type'] == 'theme' ? $packagePath . 'app' . DS . 'theme_' . Inflector::underscore($appName) . DS . 'Controller' . DS . 'Component' . DS : $packagePath . 'Controller' . DS . 'Component' . DS; - $Install = $this->loadInstallComponent($installComponentPath); - $r = true; - - if (method_exists($Install, 'beforeInstall')) { - $r = $Install->beforeInstall($this); - } - - if ($r === false) { - return false; - } - - /** Copy files **/ - $copyTo = ($this->options['type'] == 'module') ? ROOT . DS . 'Modules' . DS . $appName : APP . 'View' . DS . 'Themed' . DS . $appName; - $this->rcopy($packagePath, $copyTo); - - /** DB Logics **/ - $moduleData = array( - 'name' => ($this->options['type'] == 'module' ? $appName : 'theme_' . Inflector::underscore($appName)), - 'type' => ($this->options['type'] == 'module' ? 'module' : 'theme' ), - 'status' => intval($this->options['status']) - ); - - $this->Controller->Module->save($moduleData); # register module - - /** Build ACOS && Register module in core **/ - switch ($this->options['type']) { - case 'module': - $this->buildAcos($appName); - break; - - case 'theme': - $this->buildAcos( - 'theme_' . Inflector::underscore($appName), - APP . 'View'. DS . 'Themed' . DS . Inflector::camelize($appName) . DS . 'app' . DS - ); - - App::build(array('plugins' => array(APP . 'View'. DS . 'Themed' . DS . Inflector::camelize($appName) . DS . 'app' . DS))); - break; - } - - /** Delete unziped package **/ - $Folder->delete($workingDir); - - /** Finish **/ - if (method_exists($Install, 'afterInstall')) { - $Install->afterInstall($this); - } - - $this->afterInstall(); - } - - umask($oldMask); - - return true; - } - -/** - * Uninstall plugin by name - * - * @param string $pluginName name of the plugin to uninstall, it could be a theme plugin - * (ThemeMyThemeName or theme_my_theme_name) or module plugin - * (MyModuleName or my_module_name) - * @return boolean true on success or false otherwise - */ - public function uninstall($pluginName = false) { - if (!$pluginName || !is_string($pluginName)) { - return false; - } - - $this->options['name'] = $pluginName; - $name = Inflector::underscore($this->options['name']); - $Name = Inflector::camelize($this->options['name']); - $pData = $this->Controller->Module->findByName($name); - - if (!$pData) { - return false; - } - - /* useful for before/afterUninstall */ - $this->options['type'] = $pData['Module']['type']; - $this->options['__data'] = $pData; - $this->options['__path'] = $pData['Module']['type'] == 'theme' ? APP . 'View' . DS . 'Themed' . DS . str_replace('Theme', '', $Name) . DS . 'app' . DS . $name . DS : CakePlugin::path($Name); - $this->options['__name'] = $name; - $this->options['__Name'] = $Name; - - # core plugins can not be deleted - if (in_array($pluginName, array_merge(array('ThemeDefault', 'ThemeAdminDefault'), Configure::read('coreModules')))) { - return false; - } - - $pluginPath = $this->options['__path']; - - if (!file_exists($pluginPath)) { - return false; - } - - $Install =& $this->loadInstallComponent($pluginPath . 'Controller' . DS . 'Component' . DS); - - if (!is_object($Install)) { - return false; - } - - $r = true; - - if (method_exists($Install, 'beforeUninstall')) { - $r = $Install->beforeUninstall($this); - } - - if ($r === false) { - return false; - } - - if (!$this->Controller->Module->deleteAll(array('Module.name' => $name))) { - return false; - } - - /** - * Theme Controller does not allow to delete in-use-theme, - * but for precaution we assign to Core Default ones if for some reason - * the in-use-theme is being deleted. - */ - if ($this->options['type'] == 'theme') { - if (Configure::read('Variable.site_theme') == str_replace('Theme', '', $Name)) { - ClassRegistry::init('System.Variable')->save( - array( - 'name' => 'site_theme', - 'value' => 'Default' - ) - ); - } elseif (Configure::read('Variable.admin_theme') == str_replace('Theme', '', $Name)) { - ClassRegistry::init('System.Variable')->save( - array( - 'name' => 'admin_theme', - 'value' => 'DefaultDefault' - ) - ); - } - } - - if (method_exists($Install, 'afterUninstall')) { - $Install->afterUninstall($this); - } - - $this->afterUninstall(); - - return true; - } - - public function enable() { - - } - - public function disable() { - - } - - public function beforeInstall() { - return true; - } - - public function beforeUninstall() { - return true; - } - - public function afterInstall() { - Cache::delete('Modules'); - Cache::delete('Variable'); - - $this->Controller->Quickapps->loadVariables(); - $this->Controller->Quickapps->loadModules(); - - return true; - } - - public function afterUninstall() { - # delete & regenerate caches - Cache::delete('Modules'); - Cache::delete('Variable'); - - $this->Controller->Quickapps->loadModules(); - $this->Controller->Quickapps->loadVariables(); - - # delete all menus created by module/theme - ClassRegistry::init('Menu.Menu')->deleteAll( - array( - 'Menu.module' => $this->options['__name'] - ) - ); - - # delete blocks - ClassRegistry::init('Block.Block')->deleteAll( - array( - 'Block.module' => $this->options['__name'] - ) - ); - - # delete acos branch - $rootAco = $this->Controller->Acl->Aco->find('first', - array( - 'conditions' => array( - 'Aco.alias' => $this->options['__Name'], - 'Aco.parent_id' => null - ) - ) - ); - - $this->Controller->Acl->Aco->delete($rootAco['Aco']['id']); - - # delete node types - ClassRegistry::init('Node.NodeType')->deleteAll( - array( - 'NodeType.module' => $this->options['__name'] - ) - ); - - # delete app folder - $folderpath = ($this->options['type'] == 'module') ? $this->options['__path'] : dirname(dirname($this->options['__path'])); - $Folder = new Folder($folderpath); - $Folder->delete(); - } - -/** - * Creates acos for especified plugin by parsing its Controller folder. - * Plugin's fields are also analyzed. - * Usage example: - * {{{ - * buildAcos('user', APP . 'Plugin' . DS); // Core plugin - * }}} - * - * @param String $plugin underscored plugin name to analyze - * @param mixed $pluginPath Optional (string) plugin full base path. If it is set to false - * then ROOT/Modules is used as default base path. - * @return void - */ - public function buildAcos($plugin, $pluginPath = false) { - $pluginPath = !$pluginPath ? ROOT . DS . 'Modules' . DS : str_replace(DS.DS, DS, $pluginPath . DS); - - if (!file_exists($pluginPath . $plugin)) { - return false; - } - - $__folder = new Folder; - $cPath = $pluginPath . $plugin . DS . 'Controller' . DS; - $__folder->path = $cPath; - $controllers = $__folder->read(); $controllers = $controllers[1]; - - if (count($controllers) === 0) { - return false; - } - - $appControllerPath = $cPath . Inflector::camelize($plugin) . 'AppController.php'; - - if (file_exists($appControllerPath)) { - include_once($appControllerPath); - } - - $this->Controller->Acl->Aco->create(); - $this->Controller->Acl->Aco->save(array('alias' => Inflector::camelize($plugin))); - - $_parent_id = $this->Controller->Acl->Aco->getInsertID(); - - foreach ($controllers as $c) { - if (strpos($c, 'AppController.php') !== false) { - continue; - } - - include_once($cPath . $c); - - $className = str_replace('.php', '', $c); - $methods = get_this_class_methods($className); - - foreach ($methods as $i => $m) { - if (strpos($m, '__') === 0 || - strpos($m, '_') === 0 || - in_array($m, array('beforeFilter', 'beforeRender', 'beforeRedirect', 'afterFilter')) - ) { # ignore private and callback methods - unset($methods[$i]); - } - } - - $this->Controller->Acl->Aco->create(); - $this->Controller->Acl->Aco->save( - array( - 'parent_id' => $_parent_id, - 'alias' => str_replace('Controller', '', $className) - ) - ); - - $parent_id = $this->Controller->Acl->Aco->getInsertID(); - - foreach ($methods as $m) { - $this->Controller->Acl->Aco->create(); - $this->Controller->Acl->Aco->save( - array( - 'parent_id' => $parent_id, - 'alias' => $m - ) - ); - } - } - - # Fields - if (file_exists($pluginPath . $plugin . DS . 'Fields')) { - $__folder->path = $pluginPath . $plugin . DS . 'Fields' . DS; - $fieldsFolders = $__folder->read(); $fieldsFolders = $fieldsFolders[0]; - - foreach ($fieldsFolders as $field) { - $this->buildAcos(basename($field), $pluginPath . $plugin . DS . 'Fields' . DS); - } - } - } - -/** - * By: Drupal - * Parse a dependency for comparison by checkIncompatibility(). - * - * @param $dependency - * A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'. - * @return - * An associative array with three keys: - * - 'name' includes the name of the thing to depend on (e.g. 'foo'). - * - 'original_version' contains the original version string (which can be - * used in the UI for reporting incompatibilities). - * - 'versions' is a list of associative arrays, each containing the keys - * 'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<', - * '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'. - * Callers should pass this structure to checkIncompatibility(). - * - * @see checkIncompatibility() - */ - public function parseDependency($dependency) { - // We use named subpatterns and support every op that version_compare - // supports. Also, op is optional and defaults to equals. - $p_op = '(?P!=|==|=|<|<=|>|>=|<>)?'; - // Core version is always optional: 7.x-2.x and 2.x is treated the same. - $p_core = '(?:' . preg_quote(Configure::read('Variable.qa_version')) . '-)?'; - $p_major = '(?P\d+)'; - // By setting the minor version to x, branches can be matched. - $p_minor = '(?P(?:\d+|x)(?:-[A-Za-z]+\d+)?)'; - $value = array(); - $parts = explode('(', $dependency, 2); - $value['name'] = trim($parts[0]); - - if (isset($parts[1])) { - $value['original_version'] = ' (' . $parts[1]; - - foreach (explode(',', $parts[1]) as $version) { - if (preg_match("/^\s*{$p_op}\s*{$p_core}{$p_major}\.{$p_minor}/", $version, $matches)) { - $op = !empty($matches['operation']) ? $matches['operation'] : '='; - - if ($matches['minor'] == 'x') { - if ($op == '>' || $op == '<=') { - $matches['major']++; - } - - if ($op == '=' || $op == '==') { - $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x'); - $op = '>='; - } - } - - $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']); - } - } - } - - return $value; - } - -/** - * By: Drupal - * Check whether a version is compatible with a given dependency. - * - * @param $v - * The parsed dependency structure from parseDependency(). - * @param $current_version - * The version to check against (like 4.2). - * @return - * NULL if compatible, otherwise the original dependency version string that - * caused the incompatibility. - * - * @see parseDependency() - */ - public function checkIncompatibility($v, $current_version) { - if (!empty($v['versions'])) { - foreach ($v['versions'] as $required_version) { - if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op']))) { - return $v['original_version']; - } - } - } - - return null; - } - -/** - * Verify if all the plugins that $plugin depends on are available and match the required version. - * - * @param string $plugin plugin alias - * @return boolean - */ - public function checkDependency($plugin = null) { - $Plugin = !is_null($plugin) && isset($plugin['yaml']) ? $plugin : Configure::read('Modules.' . Inflector::underscore($plugin)); - - if (isset($Plugin['yaml']['dependencies']) && is_array($Plugin['yaml']['dependencies'])) { - foreach ($Plugin['yaml']['dependencies'] as $p) { - $check = false; - $check = Configure::read('Modules.' . Inflector::underscore($p)); - - if (!$check) { - return false; - } - - $check = $this->checkIncompatibility($this->parseDependency($p), $check['yaml']['version']); - - if (!$check) { - return false; - } - } - } - - return true; - } - -/** - * Verify if there is any plugin that depends of $plugin - * - * @param string $plugin plugin alias - * @param boolean $returnList set to true to return an array list of all plugins that uses $plugin. - * The array list contains all the plugin information Configure::read('Modules.{plugin}') - * @return mixed boolean (if $returnList = false), false return means that there are no plugins that uses $plugin. - * Or an array list of all plugins that uses $plugin ($returnList = true), empty arrray is returned - * if there are no plugins that uses $plugin. - */ - function checkReverseDependency($plugin, $returnList = true) { - $list = array(); - $plugin = Inflector::underscore($plugin); - - foreach (Configure::read('Modules') as $p) { - if (isset($p['yaml']['dependencies']) && - is_array($p['yaml']['dependencies']) - ) { - $dependencies = array(); - - foreach ($p['yaml']['dependencies'] as $d) { - $dependencies[] = $this->parseDependency($d); - } - - $dependencies = Set::extract('{n}.name', $dependencies); - - if (in_array($plugin, $dependencies, true) && $returnList) { - $list[] = $p; - } elseif (in_array($plugin, $dependencies, true)) { - return true; - } - } - } - - if ($returnList) { - return $list; - } - - return false; - } - - private function __process_tests($tests, $header = false) { - $e = 0; - - foreach ($tests as $key => $test) { - if (!$test['test']) { - $e++; - $this->errors[] = $header ? "{$test['header']}

    {$test['msg']}

    " : "

    {$test['msg']}

    "; - } - } - - return ($e == 0); - } - - public function loadInstallComponent($search = false) { - if (!file_exists($search . 'InstallComponent.php')) { - return false; - } - - include_once($search . 'InstallComponent.php'); - - $class = "InstallComponent"; - $component = new $class($this->Controller->Components); - - if (method_exists($component, 'initialize')) { - $component->initialize($this); - } - - if (method_exists($component, 'startup')) { - $component->startup($this); - } - - return $component; - } - - public function rcopy($src, $dst) { - $dir = opendir($src); - - @mkdir($dst); - - while(false !== ($file = readdir($dir))) { - if (($file != '.') && ($file != '..')) { - if (is_dir($src . DS . $file)) { - $this->rcopy($src . DS . $file,$dst . DS . $file); - } else { - if (!copy($src . DS . $file, $dst . DS . $file)) { - return false; - } - } - } - } - - closedir($dir); - } -} \ No newline at end of file diff --git a/app/Controller/Component/QuickappsComponent.php b/app/Controller/Component/QuickappsComponent.php deleted file mode 100644 index b9e4a4b9..00000000 --- a/app/Controller/Component/QuickappsComponent.php +++ /dev/null @@ -1,571 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class QuickAppsComponent extends Component { - public $Controller; - - public function startup() { } - public function beforeRender() { } - public function shutdown() { } - public function beforeRedirect() {} - -/** - * Called before the Controller::beforeFilter(). - * - * @param object $controller Controller with components to initialize - * @return void - */ - public function initialize(&$Controller) { - $this->Controller =& $Controller; - - $this->loadVariables(); - $this->loadModules(); - $this->accessCheck(); - $this->setTheme(); - $this->setTimeZone(); - $this->setLanguage(); - $this->prepareContent(); - $this->siteStatus(); - $this->setCrumb(); - } - - public function siteStatus() { - if (Configure::read('Variable.site_online') != 1 && !$this->isAdmin()) { - if ($this->Controller->plugin != 'user' && - $this->Controller->request->params['controller'] != 'log' && - !in_array($this->Controller->request->params['controller'], array('login', 'logout')) - ) { - # TODO: site down throw - //throw new NotFoundException(__t('Site offline'), 503); - } - } - } - - public function setTheme() { - if (isset($this->Controller->request->params['admin']) && $this->Controller->request->params['admin'] == 1) { - $this->Controller->theme = Configure::read('Variable.admin_theme') ? Configure::read('Variable.admin_theme') : 'admin_default'; - } else { - $this->Controller->theme = Configure::read('Variable.site_theme') ? Configure::read('Variable.site_theme') : 'default'; - } - - $this->Controller->layout ='default'; - $this->Controller->viewClass= 'Theme'; - - if (file_exists(APP . 'View' . DS . 'Themed' . DS . $this->Controller->theme . DS . "{$this->Controller->theme}.yaml")) { - $yaml = Spyc::YAMLLoad(APP . 'View' . DS . 'Themed' . DS . $this->Controller->theme . DS . "{$this->Controller->theme}.yaml"); - $yaml['info']['folder'] = $this->Controller->theme; - $yaml['settings'] = Configure::read('Modules.' . Inflector::underscore("Theme{$this->Controller->theme}") . '.settings'); - - # set custom or default logo - $yaml['settings']['site_logo_url'] = isset($yaml['settings']['site_logo_url']) && !empty($yaml['settings']['site_logo_url']) ? $yaml['settings']['site_logo_url'] : '/img/logo.png'; - - # set custom or default favicon - $yaml['settings']['site_favicon_url'] = isset($yaml['settings']['site_favicon_url']) && !empty($yaml['settings']['site_favicon_url']) ? $yaml['settings']['site_logo_url'] : '/favicon.ico'; - - Configure::write('Theme', $yaml); - - foreach ($yaml['stylesheets'] as $media => $files) { - if (!isset($this->Controller->Layout['stylesheets'][$media])){ - $this->Controller->Layout['stylesheets'][$media] = array(); - } - - foreach ($files as $file) { - $this->Controller->Layout['stylesheets'][$media][] = $file; - } - } - } - - if (Configure::read('Theme.layout')) { - $this->Controller->layout = Configure::read('Theme.layout'); - } - - $this->Controller->hook('stylesheets_alter', $this->Controller->Layout['stylesheets']); # pass css list to modules if they need to alter them (add/remove) - } - - public function prepareContent() { - $theme = Router::getParam('admin') ? Configure::read('Variable.admin_theme') : Configure::read('Variable.site_theme'); - $options = array( - 'conditions' => array( - 'Block.themes_cache LIKE' => "%:{$theme}:%", # only blocks assigned to current theme - 'Block.status' => 1, - 'OR' => array( # only blocks assigned to any/current language - 'Block.locale = ' => null, - 'Block.locale =' => '', - 'Block.locale LIKE ' => '%s:3:"' . Configure::read('Variable.language.code') . '"%', - 'Block.locale' => 'a:0:{}' - ) - ) - ); - - $this->Controller->Layout['blocks'] = $this->Controller->hook('blocks_list', $options, array('collectReturn' => false)); # request blocks to block module - $this->Controller->hook('blocks_alter', $this->Controller->Layout['blocks']); # pass blocks to modules - - /* Basic js files/embed */ - $this->Controller->Layout['javascripts']['embed'][] = ' -jQuery.extend(QuickApps.settings, { - "url": "' . str_replace("//", "/", $this->Controller->here . '/') . '", - "base_url": "' . Router::url('/') . '", - "locale": {"code": "' . Configure::read('Variable.language.code') . '"} -} ); -'; - - $this->Controller->hook('javascripts_alter', $this->Controller->Layout['javascripts']); # pass js to modules - $this->Controller->paginate = array('limit' => Configure::read('Variable.rows_per_page')); - - Configure::write('Variable.qa_version', Configure::read('Modules.system.yaml.version')); - - $defaultMetaDescription = Configure::read('Variable.site_description'); - - if (!empty($defaultMetaDescription)){ - $this->Controller->Layout['meta']['description'] = $defaultMetaDescription; - } - - #auto favicon meta - if (Configure::read('Theme.settings.site_favicon')) { - $faviconURL = Configure::read('Theme.settings.site_favicon_url'); - $this->Controller->Layout['meta']['icon'] = $faviconURL && !empty($faviconURL) ? Router::url($faviconURL) : '/favicon.ico'; - } - } - - public function setLanguage() { - $urlBefore = $this->__urlChunk(); - $urlBefore = isset($urlBefore[0]) ? $urlBefore[0] : ''; - $urlBeforeT = __t($urlBefore); - - $langs = $this->Controller->Language->find('all', array('conditions' => array('status' => 1), 'order' => array('ordering' => 'ASC'))); - $installed_codes = Set::extract('/Language/code', $langs); - $lang = $this->Controller->Session->read('language'); - - Configure::write('Config.language', $lang); - - $last_i18n_urlT = __t($this->Controller->Session->read('last_i18n_url')); - - $lang = isset($this->Controller->request->params['named']['lang']) ? $this->Controller->request->params['named']['lang'] : $lang; - $lang = isset($this->Controller->request->query['lang']) && !empty($this->Controller->request->query['lang']) ? $this->Controller->request->query['lang'] : $lang; - $lang = empty($lang) ? Configure::read('Variable.default_language') : $lang; - $lang = empty($lang) || !in_array($lang, $installed_codes) || strlen($lang) != 3 ? 'eng' : $lang; - - $this->Controller->Session->write('language', $lang); - $_lang = Set::extract("/Language[code={$lang}]/..", $langs); - - if (!isset($_lang[0]['Language'])) { # not defined -> default = english - $_lang[0]['Language'] = array( - 'code' => 'eng', - 'name' => 'English', - 'native' => 'English', - 'direction' => 'ltr' - ); - } - - Configure::write('Variable.language', $_lang[0]['Language']); - Configure::write('Variable.languages', $langs); - Configure::write('Config.language', Configure::read('Variable.language.code')); - - $urlAfter = $this->__urlChunk(); - $urlAfter = isset($urlAfter[0]) ? $urlAfter[0] : ''; - $urlAfterT = __t($urlAfter); - - if ($urlBeforeT != $urlAfterT) { - $this->Controller->Session->write('last_i18n_url', $urlBefore); - $this->Controller->redirect($urlAfterT); - } - - if (isset($this->Controller->request->params['named']['lang']) || (isset($this->Controller->request->query['lang']) && !empty($this->Controller->request->query['lang']))) { - $last_i18n_url = $this->Controller->Session->read('last_i18n_url'); - - if ($last_i18n_url && $last_i18n_urlT == $urlAfterT) { - $this->Controller->redirect($last_i18n_url); - } - } - } - - public function accessCheck() { - $this->Controller->Auth->authenticate = array( - 'Form' => array( - 'fields' => array( - 'username' => 'username', - 'password' => 'password' - ), - 'userModel' => 'User.User', - 'scope' => array('User.status' => 1) - ) - ); - - $this->Controller->Auth->loginAction = array( - 'controller' => 'user', - 'action' => 'login', - 'plugin' => 'user' - ); - - $this->Controller->Auth->authError = ''; - $this->Controller->Auth->authorize = array('Controller'); - $this->Controller->Auth->loginRedirect = Router::getParam('admin') ? '/admin' : '/'; - $this->Controller->Auth->logoutRedirect = $this->Controller->Auth->loginRedirect; - $this->Controller->Auth->allowedActions = array('login', 'logout'); - $cookie = $this->Controller->Cookie->read('UserLogin'); - - if (isset($this->Controller->request->params['plugin'])) { - $p = Configure::read("Modules.{$this->Controller->request->params['plugin']}"); - - if (!$p || $p['status'] != 1) { - $this->Controller->Auth->allowedActions = array(); - return; - } - } - - if (!$this->Controller->Auth->user() && - isset($cookie['id']) && - !empty($cookie['id']) && - isset($cookie['password']) && - !empty($cookie['password']) - ) { - $User = ClassRegistry::init('User.User'); - $User->unbindFields(); - $user = $User->find('first', - array( - 'conditions' => array( - 'User.id' => @$cookie['id'], - 'User.password' => @$cookie['password'] - ) - ) - ); - - $User->bindFields(); - - if ($user) { - $this->Controller->loadModel('UsersRole'); - $session = $user['User']; - - $session['role_id'] = $this->Controller->UsersRole->find('all', - array( - 'conditions' => array('UsersRole.user_id' => $user['User']['id']), - 'fields' => array('role_id', 'user_id') - ) - ); - - $session['role_id'] = Set::extract('/UsersRole/role_id', $session['role_id']); - $session['role_id'][] = 2; #role: authenticated user - $this->Controller->Auth->login($session); - - return true; - } - } - - if ($this->isAdmin()) { - $this->Controller->Auth->allowedActions = array('*'); - } else { - $roleId = $this->Controller->Auth->user() ? $this->Controller->Auth->user('role_id') : 3; # 3: anonymous user (public) - $aro = $this->Controller->Acl->Aro->find('first', - array( - 'conditions' => array( - 'Aro.model' => 'User.Role', - 'Aro.foreign_key' => $roleId, # roles! array of ids - ), - 'recursive' => -1, - ) - ); - $aroId = $aro['Aro']['id']; - - # get current plugin ACO - $pluginNode = $this->Controller->Acl->Aco->find('first', - array( - 'conditions' => array( - 'Aco.alias' => $this->Controller->params['plugin'], - 'parent_id = ' => null - ), - 'fields' => array('alias', 'id') - ) - ); - - # get plugin controllers ACOs - $thisControllerNode = $this->Controller->Acl->Aco->find('first', - array( - 'conditions' => array( - 'alias' => $this->Controller->name, - 'parent_id' => $pluginNode['Aco']['id'] - ) - ) - ); - - if ($thisControllerNode) { - $thisControllerActions = $this->Controller->Acl->Aco->find('list', - array( - 'conditions' => array( - 'Aco.parent_id' => $thisControllerNode['Aco']['id'], - ), - 'fields' => array( - 'Aco.id', - 'Aco.alias', - ), - 'recursive' => -1, - ) - ); - $thisControllerActionsIds = array_keys($thisControllerActions); - $allowedActions = $this->Controller->Acl->Aco->Permission->find('list', - array( - 'conditions' => array( - 'Permission.aro_id' => $aroId, - 'Permission.aco_id' => $thisControllerActionsIds, - 'Permission._create' => 1, - 'Permission._read' => 1, - 'Permission._update' => 1, - 'Permission._delete' => 1, - ), - 'fields' => array('id', 'aco_id'), - 'recursive' => -1, - ) - ); - $allowedActionsIds = array_values($allowedActions); - } - - $allow = array(); - - if (isset($allowedActionsIds) && is_array($allowedActionsIds) && count($allowedActionsIds) > 0) { - foreach ($allowedActionsIds as $i => $aId){ - $allow[] = $thisControllerActions[$aId]; - } - } - - $this->Controller->Auth->allowedActions = array_merge($this->Controller->Auth->allowedActions, $allow); - } - } - - public function setTimeZone() { - return date_default_timezone_set(Configure::read('Variable.date_default_timezone')); - } - - public function loadVariables() { - $variables = Cache::read('Variable'); - - if ($variables === false) { - $this->Controller->Variable->writeCache(); - } else { - Configure::write('Variable', $variables); - } - } - -/** - * shortcut for $this->set(`title_for_layout`...) - * - * @param string $str layout title - * @return void - */ - public function title($str) { - $this->Controller->set('title_for_layout', $str); - } - -/** - * shortcut for Session setFlash - * - * @param string $msg mesagge to display - * @param string $class type of message: error, success, alert, bubble - * @return void - */ - public function flashMsg($msg, $class = 'success') { - return $this->Controller->Session->setFlash($msg, 'default', array('class' => $class)); - } - -/** - * Set crumb from url parse or add url to the links list - * - * @param mixed $url if is array then will push de formated array to the crumbs list - * else will set base crum from string parsing - * @return void - */ - public function setCrumb($url = false) { - if (is_array($url) && !empty($url)) { - if (is_array($url[0])) { - foreach ($url as $link) { - - if (empty($link)) { - continue; - } - - $push = array( - 'MenuLink' => array( - 'link_title' => $link[0], - 'router_path' => (empty($link[1]) ? 'javascript:return false;': $link[1]), - 'description' => (isset($link[2]) ? $link[2] : ''), - ) - ); - - $this->Controller->viewVars['breadCrumb'][] = $push; - } - } else { - $push = array( - 'MenuLink' => array( - 'link_title' => $url[0], - 'router_path' => (empty($url[1]) ? 'javascript:return false;': $url[1]), - 'description' => (isset($url[2]) ? $url[2] : ''), - ) - ); - $this->Controller->viewVars['breadCrumb'][] = $push; - } - - return; - } else { - $url = !is_string($url) ? $this->__urlChunk() : $url; - } - - if (is_array($url)) { - foreach ($url as $k => $u) { - $url[$k] = preg_replace('/\/{2,}/', '', "{$u}//"); - } - } else { - $url = preg_replace('/\/{2,}/', '', "{$url}//"); - } - - $this->Controller->set('breadCrumb', array()); - - $current = $this->Controller->MenuLink->find('first', - array( - 'conditions' => array( - 'MenuLink.router_path' => (empty($url) ? '': $url) - ) - ) - ); - - if (!empty($current)) { - $this->Controller->MenuLink->Behaviors->detach('Tree'); - $this->Controller->MenuLink->Behaviors->attach('Tree', - array( - 'parent' => 'parent_id', - 'left' => 'lft', - 'right' => 'rght', - 'scope' => "MenuLink.menu_id = '{$current['MenuLink']['menu_id']}'" - ) - ); - - $path = $this->Controller->MenuLink->getPath($current['MenuLink']['id']); - - if (isset($path[0]['MenuLink']['link_title'])) { - $path[0]['MenuLink']['link_title'] = __t($path[0]['MenuLink']['link_title']); - } - - $this->Controller->set('breadCrumb', $path); - - } - - return; - } - -/** - * Insert custom block in stack - * - * @param array $data formatted block array - * @param string $region theme region where to push - * @return boolean - */ - public function blockPush($block = array(), $region = null, $show_on = true) { - if (!$show_on) { - return; - } - - $_block = array( - 'title' => '', - 'pages' => '', - 'visibility' => 0, - 'body' => '', # - 'region' => null, - 'format' => null # - ); - - $block = array_merge($_block, $block); - $block['module'] = null; - $block['id'] = null; - $block['delta'] = null; - - if (!is_null($region)) { - $block['region'] = $region; - } - - if (empty($block['region']) || empty($block['body'])) { - return false; - } - - $__block = $block; - - unset($__block['format'], $__block['body'], $__block['region'], $__block['theme']); - - $Block = array( - 'Block' => $__block, - 'BlockCustom' => array( - 'body' => $block['body'], - 'format' => $block['format'] - ), - 'BlockRegion' => array( - 0 => array( - 'theme' => $this->Controller->theme, - 'region' => $block['region'] - ) - ) - ); - - $this->Controller->Layout['blocks'][] = $Block; - } - - public function loadModules() { - $modules = Cache::read('Modules'); - - if ($modules === false) { - $modules = $this->Controller->Module->find('all', array('recursive' => -1)); - - foreach ($modules as $m) { - $v = $m['Module']; - - CakePlugin::load($m['Module']['name']); - - $v['path'] = App::pluginPath($m['Module']['name']); - $yamlFile = (strpos($m['Module']['name'], 'theme_') !== false) ? dirname(dirname($v['path'])) . DS . basename(dirname(dirname($v['path']))) . '.yaml' : $v['path'] . "{$m['Module']['name']}.yaml"; - $v['yaml'] = file_exists($yamlFile) ? Spyc::YAMLLoad($yamlFile) : array(); - - Configure::write('Modules.' . $m['Module']['name'], $v); - } - - Cache::write('Modules', Configure::read('Modules')); - } else { - Configure::write('Modules', $modules); - } - } - - public function isAdmin() { - return ($this->Controller->Auth->user() && in_array(1, (array)$this->Controller->Auth->user('role_id'))); - } - - private function __urlChunk() { - $url = '/' . $this->Controller->request->url; - $out = array(); - - $out[] = $url; - - foreach ($this->Controller->request->params['named'] as $key => $val) { - $url = str_replace_once("/{$key}:{$val}", '', $url); - $out[] = $url; - } - - $out[] = $url; - - if ($this->Controller->request->params['controller'] == $this->plugin) { - $url = str_replace_once("/{$this->Controller->request->params['controller']}", '', $url); - $out[] = $url; - } else if ($this->Controller->request->params['action'] == 'index' || $this->Controller->request->params['action'] == 'admin_index') { - $url = str_replace_once("/index", '', $url); - $out[] = $url; - } - - foreach ($this->Controller->request->params['pass'] as $p) { - $url = str_replace_once("/{$p}", '', $url); - $out[] = $url; - } - - return array_unique($out); - } -} \ No newline at end of file diff --git a/app/Controller/InstallController.php b/app/Controller/InstallController.php deleted file mode 100644 index 21dcecc6..00000000 --- a/app/Controller/InstallController.php +++ /dev/null @@ -1,297 +0,0 @@ - - * @link http://cms.quickapps.es - */ -App::uses('Controller', 'Controller'); -class InstallController extends Controller { - public $name = 'Install'; - public $uses = array(); - public $components = array('Session'); - public $helpers = array('Layout', 'Html', 'Form'); - private $__defaultDbConfig = array( - 'name' => 'default', - 'datasource'=> 'Database/Mysql', - 'persistent'=> false, - 'host'=> 'localhost', - 'login'=> 'root', - 'password'=> '', - 'database'=> 'quickapps', - 'schema'=> null, - 'prefix'=> 'qa_', - 'encoding' => 'UTF8', - 'port' => '3306' - ); - - public function beforeFilter() { - $this->viewClass = 'View'; - $this->layout = 'install'; - - # already installed ? - if (file_exists(APP . DS . 'Config' . DS . 'database.php') && file_exists(APP . DS . 'Config' . DS . 'install')) { - $this->redirect('/'); - } - } - - public function index() { - $this->redirect('/install/license'); - } - - /* Step 1: License agreement */ - public function license() { - if (isset($this->data['License'])) { - $this->__stepSuccess('license'); - $this->redirect('/install/server_test'); - } - } - - /* Step 2: Server test */ - public function server_test() { - if (!$this->__stepSuccess('license', true)) { - $this->redirect('/install/license'); - } - - if (!empty($this->data['Test'])) { - $this->__stepSuccess('server_test'); - $this->redirect('/install/database'); - } - - $tests = array( - 'php' => array( - 'test' => version_compare(PHP_VERSION, '5.2.6', '>='), - 'msg' => __t('Your php version is not suported. check that your version is 5.2 or newer.') - ), - 'mysql' => array( - 'test' => (extension_loaded('mysql') || extension_loaded('mysqli')), - 'msg' => __t('MySQL extension is not loaded on your server.') - ), - 'no_safe_mode' => array( - 'test' => (ini_get('safe_mode') == false || ini_get('safe_mode') == '' || strtolower(ini_get('safe_mode')) == 'off'), - 'msg' => __t('Your server has SafeMode on, please turn it off before continue.') - ), - 'tmp_writable' => array( - 'test' => is_writable(APP . DS . 'tmp'), - 'msg' => __t('APP/tmp folder is not writable') - ), - 'cache_writable' => array( - 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache'), - 'msg' => __t('APP/tmp/cache folder is not writable') - ), - 'installer_writable' => array( - 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'installer'), - 'msg' => __t('APP/tmp/cache/installer folder is not writable') - ), - 'models_writable' => array( - 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'models'), - 'msg' => __t('APP/tmp/cache/models folder is not writable') - ), - 'persistent_writable' => array( - 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'persistent'), - 'msg' => __t('APP/tmp/cache/persistent folder is not writable') - ), - 'i18n_writable' => array( - 'test' => is_writable(APP . DS . 'tmp' . DS . 'cache' . DS . 'i18n'), - 'msg' => __t('APP/tmp/cache/i18n folder is not writable') - ), - 'Config_writable' => array( - 'test' => is_writable(APP . DS . 'Config'), - 'msg' => __t('APP/Config folder is not writable') - ), - 'core.php_writable' => array( - 'test' => is_writable(APP . DS . 'Config'), - 'msg' => __t('APP/Config/core.php file is not writable') - ) - ); - - $results = array_unique(Set::extract('{s}.test', $tests)); - - if (!(count($results) === 1 && $results[0] === true)) { - $this->set('success', false); - $this->set('tests', $tests); - } else { - $this->set('success', true); - } - } - - /* Step 3: Database */ - public function database() { - if (!$this->__stepSuccess(array('license', 'server_test'), true)) { - $this->redirect('/install/license'); - } - - if (!empty($this->data['Database'])) { - copy(APP . 'Config' . DS . 'database.php.install', APP . 'Config' . DS . 'database.php'); - App::import('Utility', 'File'); - - $file = new File(APP . 'Config' . DS . 'database.php', true); - $dbSettings = $file->read(); - $data = $this->data; - $data['Database']['datasource'] = 'Database/Mysql'; - $data['Database']['persistent'] = 'false'; - - $dbSettings = str_replace( - array( - '{db_datasource}', - '{db_persistent}', - '{db_host}', - '{db_login}', - '{db_password}', - '{db_database}', - '{db_prefix}' - ), - array( - $data['Database']['datasource'], - $data['Database']['persistent'], - $data['Database']['host'], - $data['Database']['login'], - $data['Database']['password'], - $data['Database']['database'], - $data['Database']['prefix'] - ), - $dbSettings - ); - - $this->__defaultDbConfig = Set::merge($this->__defaultDbConfig, $data['Database']); - - if ($file->write($dbSettings)) { - $MySQLConn = @mysql_connect($this->__defaultDbConfig['host'] . ':' . $this->__defaultDbConfig['port'], $this->__defaultDbConfig['login'], $this->__defaultDbConfig['password'], true); - - if (@mysql_select_db($this->__defaultDbConfig['database'], $MySQLConn)) { - @App::import('Model', 'ConnectionManager'); - @ConnectionManager::create('default'); - - $db = ConnectionManager::getDataSource('default', $this->__defaultDbConfig); - $folder = new Folder(APP . 'Config' . DS . 'Schema' . DS . 'tables' . DS); - $files = $folder->read(); - $files = $files[1]; - $execute = array(); - - foreach ($files as $sql_file) { - $file = new File(APP . 'Config' . DS . 'Schema' . DS . 'tables' . DS . $sql_file); - $sql = $file->read(); - $query = $this->__prepareDump($sql); - $execute[] = $db->execute(str_replace('#__', $data['Database']['prefix'], $query)); - } - - if (!in_array(false, array_values($execute), true)) { - # random keys values - $file = new File(APP . 'Config' . DS . 'core.php'); - - App::uses('Security', 'Utility'); - App::load('Security'); - - $salt = Security::generateAuthKey(); - $seed = mt_rand() . mt_rand(); - $contents = $file->read(); - $contents = preg_replace('/(?<=Configure::write\(\'Security.salt\', \')([^\' ]+)(?=\'\))/', $salt, $contents); - $contents = preg_replace('/(?<=Configure::write\(\'Security.cipherSeed\', \')(\d+)(?=\'\))/', $seed, $contents); - - $file->write($contents); - Cache::write('QaInstallDatabase', 'success'); # fix: Security keys change - $this->redirect('/install/user_account'); - } else { - $this->Session->setFlash(__t('Could not dump database'), 'default', 'error'); - } - } else { - $file->close(); - unlink(APP . 'Config' . DS . 'database.php'); - $this->Session->setFlash(__t('Could not connect to database.'), 'default', 'error'); - } - } else { - $this->Session->setFlash(__t('Could not write database.php file.'), 'default', 'error'); - } - } - } - - /* Step 4: User account */ - public function user_account() { - if (Cache::read('QaInstallDatabase') == 'success' || - $this->__stepSuccess(array('license', 'server_test', 'database'), true) - ) { - $this->__stepSuccess('license'); - $this->__stepSuccess('server_test'); - $this->__stepSuccess('database'); - - Cache::delete('QaInstallDatabase'); - } else { - $this->redirect('/install/license'); - } - - if (isset($this->data['User'])) { - $this->loadModel('User.User'); - $data = $this->data; - $data['User']['status'] = 1; - $data['Role']['Role'] = array(1); - - if ($this->User->save($data)) { - $this->__stepSuccess('user_account'); - $this->redirect('/install/finish'); - } else { - $errors = ''; - - foreach ($this->User->invalidFields() as $field => $error) { - $errors .= "{$field}: {$error}
    "; - } - - $this->Session->setFlash( - '' . __t('Could not create new user, please try again.') . "
    " . - $errors - , 'default', 'error'); - } - } - } - - /* Step 5: Finish */ - public function finish() { - if (!$this->__stepSuccess(array('license', 'server_test', 'database', 'user_account'), true)) { - $this->redirect('/install/license'); - } - - App::import('Utility', 'File'); - - $file = new File(APP . 'Config' . DS . 'install', true); - - if ($file->write(time())) { - $this->__stepSuccess('finish'); - $this->Session->delete('QaInstall'); - $this->redirect('/admin'); - } else { - $this->Session->setFlash(__t("Could not write 'install' file. Check file/folder permissions and refresh this page"), 'default', 'error'); - } - } - - private function __stepSuccess($step, $check = false) { - if (!$check) { - return $this->Session->write("QaInstall.{$step}", 'success'); - } - - if (is_array($step)) { - foreach ($step as $s) { - if (!$this->Session->check("QaInstall.{$s}")) { - return false; - } - } - - return true; - } else { - return $this->Session->check("QaInstall.{$step}"); - } - - return false; - } - - private function __prepareDump($sql) { - $sql = trim($sql); - $sql = ereg_replace("\n#[^\n]*\n", "\n", $sql); - $sql = preg_replace('/^\-\-(.*)/im', '', $sql); - $sql = preg_replace("/\n{2,}/m", "\n", $sql); - - return $sql; - } -} \ No newline at end of file diff --git a/app/Lib/Model/Model.php b/app/Lib/Model/Model.php deleted file mode 100644 index 0b8f1027..00000000 --- a/app/Lib/Model/Model.php +++ /dev/null @@ -1,3433 +0,0 @@ - table 'users'; class 'Man' => table 'men') - * The table is required to have at least 'id auto_increment' primary key. - * - * @package Cake.Model - * @link http://book.cakephp.org/2.0/en/models.html - */ -class Model extends Object { - -/** - * The name of the DataSource connection that this Model uses - * - * The value must be an attribute name that you defined in `app/Config/database.php` - * or created using `ConnectionManager::create()`. - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#usedbconfig - */ - public $useDbConfig = 'default'; - -/** - * Custom database table name, or null/false if no table association is desired. - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#useTable - */ - public $useTable = null; - -/** - * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements. - * - * This field is also used in `find('list')` when called with no extra parameters in the fields list - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#displayField - */ - public $displayField = null; - -/** - * Value of the primary key ID of the record that this model is currently pointing to. - * Automatically set after database insertions. - * - * @var mixed - */ - public $id = false; - -/** - * Container for the data that this model gets from persistent storage (usually, a database). - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#data - */ - public $data = array(); - -/** - * Table name for this Model. - * - * @var string - */ - public $table = false; - -/** - * The name of the primary key field for this model. - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#primaryKey - */ - public $primaryKey = null; - -/** - * Field-by-field table metadata. - * - * @var array - */ - protected $_schema = null; - -/** - * List of validation rules. It must be an array with the field name as key and using - * as value one of the following possibilities - * - * ### Validating using regular expressions - * - * {{{ - * public $validate = array( - * 'name' => '/^[a-z].+$/i' - * ); - * }}} - * - * ### Validating using methods (no parameters) - * - * {{{ - * public $validate = array( - * 'name' => 'notEmpty' - * ); - * }}} - * - * ### Validating using methods (with parameters) - * - * {{{ - * public $validate = array( - * 'age' => array( - * 'rule' => array('between', 5, 25) - * ) - * ); - * }}} - * - * ### Validating using custom method - * - * {{{ - * public $validate = array( - * 'password' => array( - * 'rule' => array('customValidation') - * ) - * ); - * public function customValidation($data) { - * // $data will contain array('password' => 'value') - * if (isset($this->data[$this->alias]['password2'])) { - * return $this->data[$this->alias]['password2'] === current($data); - * } - * return true; - * } - * }}} - * - * ### Validations with messages - * - * The messages will be used in Model::$validationErrors and can be used in the FormHelper - * - * {{{ - * public $validate = array( - * 'age' => array( - * 'rule' => array('between', 5, 25), - * 'message' => array('The age must be between %d and %d.') - * ) - * ); - * }}} - * - * ### Multiple validations to the same field - * - * {{{ - * public $validate = array( - * 'login' => array( - * array( - * 'rule' => 'alphaNumeric', - * 'message' => 'Only alphabets and numbers allowed', - * 'last' => true - * ), - * array( - * 'rule' => array('minLength', 8), - * 'message' => array('Minimum length of %d characters') - * ) - * ) - * ); - * }}} - * - * ### Valid keys in validations - * - * - `rule`: String with method name, regular expression (started by slash) or array with method and parameters - * - `message`: String with the message or array if have multiple parameters. See http://php.net/sprintf - * - `last`: Boolean value to indicate if continue validating the others rules if the current fail [Default: true] - * - `required`: Boolean value to indicate if the field must be present on save - * - `allowEmpty`: Boolean value to indicate if the field can be empty - * - `on`: Possible values: `update`, `create`. Indicate to apply this rule only on update or create - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#validate - * @link http://book.cakephp.org/2.0/en/models/data-validation.html - */ - public $validate = array(); - -/** - * List of validation errors. - * - * @var array - */ - public $validationErrors = array(); - - -/** - * Name of the validation string domain to use when translating validation errors. - * - * @var string - */ - public $validationDomain = null; - -/** - * Database table prefix for tables in model. - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#tableprefix - */ - public $tablePrefix = null; - -/** - * Name of the model. - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#name - */ - public $name = null; - -/** - * Alias name for model. - * - * @var string - */ - public $alias = null; - -/** - * List of table names included in the model description. Used for associations. - * - * @var array - */ - public $tableToModel = array(); - -/** - * Whether or not to cache queries for this model. This enables in-memory - * caching only, the results are not stored beyond the current request. - * - * @var boolean - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#cacheQueries - */ - public $cacheQueries = false; - -/** - * Detailed list of belongsTo associations. - * - * ### Basic usage - * - * `public $belongsTo = array('Group', 'Department');` - * - * ### Detailed configuration - * - * {{{ - * public $belongsTo = array( - * 'Group', - * 'Department' => array( - * 'className' => 'Department', - * 'foreignKey' => 'department_id' - * ) - * ); - * }}} - * - * ### Possible keys in association - * - * - `className`: the classname of the model being associated to the current model. - * If you’re defining a ‘Profile belongsTo User’ relationship, the className key should equal ‘User.’ - * - `foreignKey`: the name of the foreign key found in the current model. This is - * especially handy if you need to define multiple belongsTo relationships. The default - * value for this key is the underscored, singular name of the other model, suffixed with ‘_id’. - * - `conditions`: An SQL fragment used to filter related model records. It’s good - * practice to use model names in SQL fragments: “User.active = 1†is always - * better than just “active = 1.†- * - `type`: the type of the join to use in the SQL query, default is LEFT which - * may not fit your needs in all situations, INNER may be helpful when you want - * everything from your main and associated models or nothing at all!(effective - * when used with some conditions of course). (NB: type value is in lower case - i.e. left, inner) - * - `fields`: A list of fields to be retrieved when the associated model data is - * fetched. Returns all fields by default. - * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. - * - `counterCache`: If set to true the associated Model will automatically increase or - * decrease the “[singular_model_name]_count†field in the foreign table whenever you do - * a save() or delete(). If its a string then its the field name to use. The value in the - * counter field represents the number of related rows. - * - `counterScope`: Optional conditions array to use for updating counter cache field. - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto - */ - public $belongsTo = array(); - -/** - * Detailed list of hasOne associations. - * - * ### Basic usage - * - * `public $hasOne = array('Profile', 'Address');` - * - * ### Detailed configuration - * - * {{{ - * public $hasOne = array( - * 'Profile', - * 'Address' => array( - * 'className' => 'Address', - * 'foreignKey' => 'user_id' - * ) - * ); - * }}} - * - * ### Possible keys in association - * - * - `className`: the classname of the model being associated to the current model. - * If you’re defining a ‘User hasOne Profile’ relationship, the className key should equal ‘Profile.’ - * - `foreignKey`: the name of the foreign key found in the other model. This is - * especially handy if you need to define multiple hasOne relationships. - * The default value for this key is the underscored, singular name of the - * current model, suffixed with ‘_id’. In the example above it would default to 'user_id'. - * - `conditions`: An SQL fragment used to filter related model records. It’s good - * practice to use model names in SQL fragments: “Profile.approved = 1†is - * always better than just “approved = 1.†- * - `fields`: A list of fields to be retrieved when the associated model data is - * fetched. Returns all fields by default. - * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. - * - `dependent`: When the dependent key is set to true, and the model’s delete() - * method is called with the cascade parameter set to true, associated model - * records are also deleted. In this case we set it true so that deleting a - * User will also delete her associated Profile. - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasone - */ - public $hasOne = array(); - -/** - * Detailed list of hasMany associations. - * - * ### Basic usage - * - * `public $hasMany = array('Comment', 'Task');` - * - * ### Detailed configuration - * - * {{{ - * public $hasMany = array( - * 'Comment', - * 'Task' => array( - * 'className' => 'Task', - * 'foreignKey' => 'user_id' - * ) - * ); - * }}} - * - * ### Possible keys in association - * - * - `className`: the classname of the model being associated to the current model. - * If you’re defining a ‘User hasMany Comment’ relationship, the className key should equal ‘Comment.’ - * - `foreignKey`: the name of the foreign key found in the other model. This is - * especially handy if you need to define multiple hasMany relationships. The default - * value for this key is the underscored, singular name of the actual model, suffixed with ‘_id’. - * - `conditions`: An SQL fragment used to filter related model records. It’s good - * practice to use model names in SQL fragments: “Comment.status = 1†is always - * better than just “status = 1.†- * - `fields`: A list of fields to be retrieved when the associated model data is - * fetched. Returns all fields by default. - * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. - * - `limit`: The maximum number of associated rows you want returned. - * - `offset`: The number of associated rows to skip over (given the current - * conditions and order) before fetching and associating. - * - `dependent`: When dependent is set to true, recursive model deletion is - * possible. In this example, Comment records will be deleted when their - * associated User record has been deleted. - * - `exclusive`: When exclusive is set to true, recursive model deletion does - * the delete with a deleteAll() call, instead of deleting each entity separately. - * This greatly improves performance, but may not be ideal for all circumstances. - * - `finderQuery`: A complete SQL query CakePHP can use to fetch associated model - * records. This should be used in situations that require very custom results. - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany - */ - public $hasMany = array(); - -/** - * Detailed list of hasAndBelongsToMany associations. - * - * ### Basic usage - * - * `public $hasAndBelongsToMany = array('Role', 'Address');` - * - * ### Detailed configuration - * - * {{{ - * public $hasAndBelongsToMany = array( - * 'Role', - * 'Address' => array( - * 'className' => 'Address', - * 'foreignKey' => 'user_id', - * 'associationForeignKey' => 'address_id', - * 'joinTable' => 'addresses_users' - * ) - * ); - * }}} - * - * ### Possible keys in association - * - * - `className`: the classname of the model being associated to the current model. - * If you're defining a ‘Recipe HABTM Tag' relationship, the className key should equal ‘Tag.' - * - `joinTable`: The name of the join table used in this association (if the - * current table doesn't adhere to the naming convention for HABTM join tables). - * - `with`: Defines the name of the model for the join table. By default CakePHP - * will auto-create a model for you. Using the example above it would be called - * RecipesTag. By using this key you can override this default name. The join - * table model can be used just like any "regular" model to access the join table directly. - * - `foreignKey`: the name of the foreign key found in the current model. - * This is especially handy if you need to define multiple HABTM relationships. - * The default value for this key is the underscored, singular name of the - * current model, suffixed with ‘_id'. - * - `associationForeignKey`: the name of the foreign key found in the other model. - * This is especially handy if you need to define multiple HABTM relationships. - * The default value for this key is the underscored, singular name of the other - * model, suffixed with ‘_id'. - * - `unique`: If true (default value) cake will first delete existing relationship - * records in the foreign keys table before inserting new ones, when updating a - * record. So existing associations need to be passed again when updating. - * - `conditions`: An SQL fragment used to filter related model records. It's good - * practice to use model names in SQL fragments: "Comment.status = 1" is always - * better than just "status = 1." - * - `fields`: A list of fields to be retrieved when the associated model data is - * fetched. Returns all fields by default. - * - `order`: An SQL fragment that defines the sorting order for the returned associated rows. - * - `limit`: The maximum number of associated rows you want returned. - * - `offset`: The number of associated rows to skip over (given the current - * conditions and order) before fetching and associating. - * - `finderQuery`, `deleteQuery`, `insertQuery`: A complete SQL query CakePHP - * can use to fetch, delete, or create new associated model records. This should - * be used in situations that require very custom results. - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm - */ - public $hasAndBelongsToMany = array(); - -/** - * List of behaviors to load when the model object is initialized. Settings can be - * passed to behaviors by using the behavior name as index. Eg: - * - * public $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1')) - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/behaviors.html#using-behaviors - */ - public $actsAs = null; - -/** - * Holds the Behavior objects currently bound to this model. - * - * @var BehaviorCollection - */ - public $Behaviors = null; - -/** - * Whitelist of fields allowed to be saved. - * - * @var array - */ - public $whitelist = array(); - -/** - * Whether or not to cache sources for this model. - * - * @var boolean - */ - public $cacheSources = true; - -/** - * Type of find query currently executing. - * - * @var string - */ - public $findQueryType = null; - -/** - * Number of associations to recurse through during find calls. Fetches only - * the first level by default. - * - * @var integer - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive - */ - public $recursive = 1; - -/** - * The column name(s) and direction(s) to order find results by default. - * - * public $order = "Post.created DESC"; - * public $order = array("Post.view_count DESC", "Post.rating DESC"); - * - * @var string - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#order - */ - public $order = null; - -/** - * Array of virtual fields this model has. Virtual fields are aliased - * SQL expressions. Fields added to this property will be read as other fields in a model - * but will not be saveable. - * - * `public $virtualFields = array('two' => '1 + 1');` - * - * Is a simplistic example of how to set virtualFields - * - * @var array - * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#virtualfields - */ - public $virtualFields = array(); - -/** - * Default list of association keys. - * - * @var array - */ - protected $_associationKeys = array( - 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'), - 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'), - 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'), - 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery') - ); - -/** - * Holds provided/generated association key names and other data for all associations. - * - * @var array - */ - protected $_associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); - -/** - * Holds model associations temporarily to allow for dynamic (un)binding. - * - * @var array - */ - public $__backAssociation = array(); - -/** - * Back inner association - * - * @var array - */ - public $__backInnerAssociation = array(); - -/** - * Back original association - * - * @var array - */ - public $__backOriginalAssociation = array(); - -/** - * Back containable association - * - * @var array - */ - public $__backContainableAssociation = array(); - -/** - * The ID of the model record that was last inserted. - * - * @var integer - */ - protected $_insertID = null; - -/** - * Has the datasource been configured. - * - * @var boolean - * @see Model::getDataSource - */ - protected $_sourceConfigured = false; - -/** - * List of valid finder method options, supplied as the first parameter to find(). - * - * @var array - */ - public $findMethods = array( - 'all' => true, 'first' => true, 'count' => true, - 'neighbors' => true, 'list' => true, 'threaded' => true - ); - -/** - * Constructor. Binds the model's database table to the object. - * - * If `$id` is an array it can be used to pass several options into the model. - * - * - id - The id to start the model on. - * - table - The table to use for this model. - * - ds - The connection name this model is connected to. - * - name - The name of the model eg. Post. - * - alias - The alias of the model, this is used for registering the instance in the `ClassRegistry`. - * eg. `ParentThread` - * - * ### Overriding Model's __construct method. - * - * When overriding Model::__construct() be careful to include and pass in all 3 of the - * arguments to `parent::__construct($id, $table, $ds);` - * - * ### Dynamically creating models - * - * You can dynamically create model instances using the $id array syntax. - * - * {{{ - * $Post = new Model(array('table' => 'posts', 'name' => 'Post', 'ds' => 'connection2')); - * }}} - * - * Would create a model attached to the posts table on connection2. Dynamic model creation is useful - * when you want a model object that contains no associations or attached behaviors. - * - * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above. - * @param string $table Name of database table to use. - * @param string $ds DataSource connection name. - */ - public function __construct($id = false, $table = null, $ds = null) { - parent::__construct(); - - if (is_array($id)) { - extract(array_merge( - array( - 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig, - 'name' => $this->name, 'alias' => $this->alias - ), - $id - )); - } - - if ($this->name === null) { - $this->name = (isset($name) ? $name : get_class($this)); - } - - if ($this->alias === null) { - $this->alias = (isset($alias) ? $alias : $this->name); - } - - if ($this->primaryKey === null) { - $this->primaryKey = 'id'; - } - - ClassRegistry::addObject($this->alias, $this); - - $this->id = $id; - unset($id); - - if ($table === false) { - $this->useTable = false; - } elseif ($table) { - $this->useTable = $table; - } - - if ($ds !== null) { - $this->useDbConfig = $ds; - } - - if (is_subclass_of($this, 'AppModel')) { - $merge = array('findMethods'); - if ($this->actsAs !== null || $this->actsAs !== false) { - $merge[] = 'actsAs'; - } - $parentClass = get_parent_class($this); - if ($parentClass !== 'AppModel') { - $this->_mergeVars($merge, $parentClass); - } - $this->_mergeVars($merge, 'AppModel'); - } - $this->Behaviors = new BehaviorCollection(); - - if ($this->useTable !== false) { - - if ($this->useTable === null) { - $this->useTable = Inflector::tableize($this->name); - } - - if ($this->displayField == null) { - unset($this->displayField); - } - $this->table = $this->useTable; - $this->tableToModel[$this->table] = $this->alias; - } elseif ($this->table === false) { - $this->table = Inflector::tableize($this->name); - } - $this->_createLinks(); - $this->Behaviors->init($this->alias, $this->actsAs); - } - -/** - * Handles custom method calls, like findBy for DB models, - * and custom RPC calls for remote data sources. - * - * @param string $method Name of method to call. - * @param array $params Parameters for the method. - * @return mixed Whatever is returned by called method - */ - public function __call($method, $params) { - $result = $this->Behaviors->dispatchMethod($this, $method, $params); - if ($result !== array('unhandled')) { - return $result; - } - $return = $this->getDataSource()->query($method, $params, $this); - return $return; - } - -/** - * Handles the lazy loading of model associations by lookin in the association arrays for the requested variable - * - * @param string $name variable tested for existance in class - * @return boolean true if the variable exists (if is a not loaded model association it will be created), false otherwise - */ - public function __isset($name) { - $className = false; - - foreach ($this->_associations as $type) { - if (isset($name, $this->{$type}[$name])) { - $className = empty($this->{$type}[$name]['className']) ? $name : $this->{$type}[$name]['className']; - break; - } - elseif (isset($name, $this->__backAssociation[$type][$name])) { - $className = empty($this->__backAssociation[$type][$name]['className']) ? - $name : $this->__backAssociation[$type][$name]['className']; - break; - } else if ($type == 'hasAndBelongsToMany') { - foreach ($this->{$type} as $k => $relation) { - if (empty($relation['with'])) { - continue; - } - if (is_array($relation['with'])) { - if (key($relation['with']) === $name) { - $className = $name; - } - } else { - list($plugin, $class) = pluginSplit($relation['with']); - if ($class === $name) { - $className = $relation['with']; - } - } - if ($className) { - $assocKey = $k; - $dynamic = !empty($relation['dynamicWith']); - break(2); - } - } - } - } - - if (!$className) { - return false; - } - - list($plugin, $className) = pluginSplit($className); - - if (!ClassRegistry::isKeySet($className) && !empty($dynamic)) { - $this->{$className} = new AppModel(array( - 'name' => $className, - 'table' => $this->hasAndBelongsToMany[$assocKey]['joinTable'], - 'ds' => $this->useDbConfig - )); - } else { - $this->_constructLinkedModel($name, $className, $plugin); - } - - if (!empty($assocKey)) { - $this->hasAndBelongsToMany[$assocKey]['joinTable'] = $this->{$name}->table; - if (count($this->{$name}->schema()) <= 2 && $this->{$name}->primaryKey !== false) { - $this->{$name}->primaryKey = $this->hasAndBelongsToMany[$assocKey]['foreignKey']; - } - } - - return true; - } - -/** - * Returns the value of the requested variable if it can be set by __isset() - * - * @param string $name variable requested for it's value or reference - * @return mixed value of requested variable if it is set - */ - public function __get($name) { - if ($name === 'displayField') { - return $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey)); - } - if (isset($this->{$name})) { - return $this->{$name}; - } - } - -/** - * Bind model associations on the fly. - * - * If `$reset` is false, association will not be reset - * to the originals defined in the model - * - * Example: Add a new hasOne binding to the Profile model not - * defined in the model source code: - * - * `$this->User->bindModel( array('hasOne' => array('Profile')) );` - * - * Bindings that are not made permanent will be reset by the next Model::find() call on this - * model. - * - * @param array $params Set of bindings (indexed by binding type) - * @param boolean $reset Set to false to make the binding permanent - * @return boolean Success - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly - */ - public function bindModel($params, $reset = true) { - foreach ($params as $assoc => $model) { - if ($reset === true && !isset($this->__backAssociation[$assoc])) { - $this->__backAssociation[$assoc] = $this->{$assoc}; - } - foreach ($model as $key => $value) { - $assocName = $key; - - if (is_numeric($key)) { - $assocName = $value; - $value = array(); - } - $modelName = $assocName; - $this->{$assoc}[$assocName] = $value; - if (property_exists($this, $assocName)) { - unset($this->{$assocName}); - } - if ($reset === false && isset($this->__backAssociation[$assoc])) { - $this->__backAssociation[$assoc][$assocName] = $value; - } - } - } - $this->_createLinks(); - return true; - } - -/** - * Turn off associations on the fly. - * - * If $reset is false, association will not be reset - * to the originals defined in the model - * - * Example: Turn off the associated Model Support request, - * to temporarily lighten the User model: - * - * `$this->User->unbindModel( array('hasMany' => array('Supportrequest')) );` - * - * unbound models that are not made permanent will reset with the next call to Model::find() - * - * @param array $params Set of bindings to unbind (indexed by binding type) - * @param boolean $reset Set to false to make the unbinding permanent - * @return boolean Success - * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly - */ - public function unbindModel($params, $reset = true) { - foreach ($params as $assoc => $models) { - if ($reset === true && !isset($this->__backAssociation[$assoc])) { - $this->__backAssociation[$assoc] = $this->{$assoc}; - } - foreach ($models as $model) { - if ($reset === false && isset($this->__backAssociation[$assoc][$model])) { - unset($this->__backAssociation[$assoc][$model]); - } - unset($this->{$assoc}[$model]); - } - } - return true; - } - -/** - * Create a set of associations. - * - * @return void - */ - protected function _createLinks() { - foreach ($this->_associations as $type) { - if (!is_array($this->{$type})) { - $this->{$type} = explode(',', $this->{$type}); - - foreach ($this->{$type} as $i => $className) { - $className = trim($className); - unset ($this->{$type}[$i]); - $this->{$type}[$className] = array(); - } - } - - if (!empty($this->{$type})) { - foreach ($this->{$type} as $assoc => $value) { - $plugin = null; - - if (is_numeric($assoc)) { - unset ($this->{$type}[$assoc]); - $assoc = $value; - $value = array(); - - if (strpos($assoc, '.') !== false) { - list($plugin, $assoc) = pluginSplit($assoc); - $this->{$type}[$assoc] = array('className' => $plugin. '.' . $assoc); - } else { - $this->{$type}[$assoc] = $value; - } - } - $this->_generateAssociation($type, $assoc); - } - } - } - } - -/** - * Protected helper method to create associated models of a given class. - * - * @param string $assoc Association name - * @param string $className Class name - * @param string $plugin name of the plugin where $className is located - * examples: public $hasMany = array('Assoc' => array('className' => 'ModelName')); - * usage: $this->Assoc->modelMethods(); - * - * public $hasMany = array('ModelName'); - * usage: $this->ModelName->modelMethods(); - * @return void - */ - protected function _constructLinkedModel($assoc, $className = null, $plugin = null) { - if (empty($className)) { - $className = $assoc; - } - - if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) { - if ($plugin) { - $plugin .= '.'; - } - $model = array('class' => $plugin . $className, 'alias' => $assoc); - $this->{$assoc} = ClassRegistry::init($model); - if ($plugin) { - ClassRegistry::addObject($plugin . $className, $this->{$assoc}); - } - if ($assoc) { - $this->tableToModel[$this->{$assoc}->table] = $assoc; - } - } - } - -/** - * Build an array-based association from string. - * - * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' - * @param string $assocKey - * @return void - */ - protected function _generateAssociation($type, $assocKey) { - $class = $assocKey; - $dynamicWith = false; - - foreach ($this->_associationKeys[$type] as $key) { - - if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) { - $data = ''; - - switch ($key) { - case 'fields': - $data = ''; - break; - - case 'foreignKey': - $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id'; - break; - - case 'associationForeignKey': - $data = Inflector::singularize($this->{$class}->table) . '_id'; - break; - - case 'with': - $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable'])); - $dynamicWith = true; - break; - - case 'joinTable': - $tables = array($this->table, $this->{$class}->table); - sort ($tables); - $data = $tables[0] . '_' . $tables[1]; - break; - - case 'className': - $data = $class; - break; - - case 'unique': - $data = true; - break; - } - $this->{$type}[$assocKey][$key] = $data; - } - - if ($dynamicWith) { - $this->{$type}[$assocKey]['dynamicWith'] = true; - } - - } - } - -/** - * Sets a custom table for your controller class. Used by your controller to select a database table. - * - * @param string $tableName Name of the custom table - * @throws MissingTableException when database table $tableName is not found on data source - * @return void - */ - public function setSource($tableName) { - $this->setDataSource($this->useDbConfig); - $db = ConnectionManager::getDataSource($this->useDbConfig); - $db->cacheSources = ($this->cacheSources && $db->cacheSources); - - if (method_exists($db, 'listSources')) { - $sources = $db->listSources(); - if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) { - throw new MissingTableException(array( - 'table' => $this->tablePrefix . $tableName, - 'class' => $this->alias - )); - } - $this->_schema = null; - } - $this->table = $this->useTable = $tableName; - $this->tableToModel[$this->table] = $this->alias; - } - -/** - * This function does two things: - * - * 1. it scans the array $one for the primary key, - * and if that's found, it sets the current id to the value of $one[id]. - * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object. - * 2. Returns an array with all of $one's keys and values. - * (Alternative indata: two strings, which are mangled to - * a one-item, two-dimensional array using $one for a key and $two as its value.) - * - * @param mixed $one Array or string of data - * @param string $two Value string for the alternative indata method - * @return array Data with all of $one's keys and values - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html - */ - public function set($one, $two = null) { - if (!$one) { - return; - } - if (is_object($one)) { - if ($one instanceof SimpleXMLElement || $one instanceof DOMNode) { - $one = $this->_normalizeXmlData(Xml::toArray($one)); - } else { - $one = Set::reverse($one); - } - } - - if (is_array($one)) { - $data = $one; - if (empty($one[$this->alias])) { - if ($this->getAssociated(key($one)) === null) { - $data = array($this->alias => $one); - } - } - } else { - $data = array($this->alias => array($one => $two)); - } - - foreach ($data as $modelName => $fieldSet) { - if (is_array($fieldSet)) { - - foreach ($fieldSet as $fieldName => $fieldValue) { - if (isset($this->validationErrors[$fieldName])) { - unset ($this->validationErrors[$fieldName]); - } - - if ($modelName === $this->alias) { - if ($fieldName === $this->primaryKey) { - $this->id = $fieldValue; - } - } - if (is_array($fieldValue) || is_object($fieldValue)) { - $fieldValue = $this->deconstruct($fieldName, $fieldValue); - } - $this->data[$modelName][$fieldName] = $fieldValue; - } - } - } - return $data; - } - -/** - * Normalize Xml::toArray() to use in Model::save() - * - * @param array $xml XML as array - * @return array - */ - protected function _normalizeXmlData(array $xml) { - $return = array(); - foreach ($xml as $key => $value) { - if (is_array($value)) { - $return[Inflector::camelize($key)] = $this->_normalizeXmlData($value); - } elseif ($key[0] === '@') { - $return[substr($key, 1)] = $value; - } else { - $return[$key] = $value; - } - } - return $return; - } - -/** - * Deconstructs a complex data type (array or object) into a single field value. - * - * @param string $field The name of the field to be deconstructed - * @param mixed $data An array or object to be deconstructed into a field - * @return mixed The resulting data that should be assigned to a field - */ - public function deconstruct($field, $data) { - if (!is_array($data)) { - return $data; - } - - $copy = $data; - $type = $this->getColumnType($field); - - if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) { - $useNewDate = (isset($data['year']) || isset($data['month']) || - isset($data['day']) || isset($data['hour']) || isset($data['minute'])); - - $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec'); - $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec'); - $date = array(); - - if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) { - $data['hour'] = $data['hour'] + 12; - } - if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) { - $data['hour'] = '00'; - } - if ($type == 'time') { - foreach ($timeFields as $key => $val) { - if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { - $data[$val] = '00'; - } elseif ($data[$val] === '') { - $data[$val] = ''; - } else { - $data[$val] = sprintf('%02d', $data[$val]); - } - if (!empty($data[$val])) { - $date[$key] = $data[$val]; - } else { - return null; - } - } - } - - if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') { - foreach ($dateFields as $key => $val) { - if ($val == 'hour' || $val == 'min' || $val == 'sec') { - if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { - $data[$val] = '00'; - } else { - $data[$val] = sprintf('%02d', $data[$val]); - } - } - if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) { - return null; - } - if (isset($data[$val]) && !empty($data[$val])) { - $date[$key] = $data[$val]; - } - } - } - - $format = $this->getDataSource()->columns[$type]['format']; - $day = empty($date['Y']) ? null : $date['Y'] . '-' . $date['m'] . '-' . $date['d'] . ' '; - $hour = empty($date['H']) ? null : $date['H'] . ':' . $date['i'] . ':' . $date['s']; - $date = new DateTime($day . $hour); - if ($useNewDate && !empty($date)) { - return $date->format($format); - } - } - return $data; - } - -/** - * Returns an array of table metadata (column names and types) from the database. - * $field => keys(type, null, default, key, length, extra) - * - * @param mixed $field Set to true to reload schema, or a string to return a specific field - * @return array Array of table metadata - */ - public function schema($field = false) { - if (!is_array($this->_schema) || $field === true) { - $db = $this->getDataSource(); - $db->cacheSources = ($this->cacheSources && $db->cacheSources); - if (method_exists($db, 'describe') && $this->useTable !== false) { - $this->_schema = $db->describe($this); - } elseif ($this->useTable === false) { - $this->_schema = array(); - } - } - if (is_string($field)) { - if (isset($this->_schema[$field])) { - return $this->_schema[$field]; - } else { - return null; - } - } - return $this->_schema; - } - -/** - * Returns an associative array of field names and column types. - * - * @return array Field types indexed by field name - */ - public function getColumnTypes() { - $columns = $this->schema(); - if (empty($columns)) { - trigger_error(__d('cake_dev', '(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()'), E_USER_WARNING); - } - $cols = array(); - foreach ($columns as $field => $values) { - $cols[$field] = $values['type']; - } - return $cols; - } - -/** - * Returns the column type of a column in the model. - * - * @param string $column The name of the model column - * @return string Column type - */ - public function getColumnType($column) { - $db = $this->getDataSource(); - $cols = $this->schema(); - $model = null; - - $column = str_replace(array($db->startQuote, $db->endQuote), '', $column); - - if (strpos($column, '.')) { - list($model, $column) = explode('.', $column); - } - if ($model != $this->alias && isset($this->{$model})) { - return $this->{$model}->getColumnType($column); - } - if (isset($cols[$column]) && isset($cols[$column]['type'])) { - return $cols[$column]['type']; - } - return null; - } - -/** - * Returns true if the supplied field exists in the model's database table. - * - * @param mixed $name Name of field to look for, or an array of names - * @param boolean $checkVirtual checks if the field is declared as virtual - * @return mixed If $name is a string, returns a boolean indicating whether the field exists. - * If $name is an array of field names, returns the first field that exists, - * or false if none exist. - */ - public function hasField($name, $checkVirtual = false) { - if (is_array($name)) { - foreach ($name as $n) { - if ($this->hasField($n, $checkVirtual)) { - return $n; - } - } - return false; - } - - if ($checkVirtual && !empty($this->virtualFields)) { - if ($this->isVirtualField($name)) { - return true; - } - } - - if (empty($this->_schema)) { - $this->schema(); - } - - if ($this->_schema != null) { - return isset($this->_schema[$name]); - } - return false; - } - -/** - * Check that a method is callable on a model. This will check both the model's own methods, its - * inherited methods and methods that could be callable through behaviors. - * - * @param string $method The method to be called. - * @return boolean True on method being callable. - */ - public function hasMethod($method) { - if (method_exists($this, $method)) { - return true; - } - if ($this->Behaviors->hasMethod($method)) { - return true; - } - return false; - } - -/** - * Returns true if the supplied field is a model Virtual Field - * - * @param string $field Name of field to look for - * @return boolean indicating whether the field exists as a model virtual field. - */ - public function isVirtualField($field) { - if (empty($this->virtualFields) || !is_string($field)) { - return false; - } - if (isset($this->virtualFields[$field])) { - return true; - } - if (strpos($field, '.') !== false) { - list($model, $field) = explode('.', $field); - if ($model == $this->alias && isset($this->virtualFields[$field])) { - return true; - } - } - return false; - } - -/** - * Returns the expression for a model virtual field - * - * @param string $field Name of field to look for - * @return mixed If $field is string expression bound to virtual field $field - * If $field is null, returns an array of all model virtual fields - * or false if none $field exist. - */ - public function getVirtualField($field = null) { - if ($field == null) { - return empty($this->virtualFields) ? false : $this->virtualFields; - } - if ($this->isVirtualField($field)) { - if (strpos($field, '.') !== false) { - list($model, $field) = explode('.', $field); - } - return $this->virtualFields[$field]; - } - return false; - } - -/** - * Initializes the model for writing a new record, loading the default values - * for those fields that are not defined in $data, and clearing previous validation errors. - * Especially helpful for saving data in loops. - * - * @param mixed $data Optional data array to assign to the model after it is created. If null or false, - * schema data defaults are not merged. - * @param boolean $filterKey If true, overwrites any primary key input with an empty value - * @return array The current Model::data; after merging $data and/or defaults from database - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-create-array-data-array - */ - public function create($data = array(), $filterKey = false) { - $defaults = array(); - $this->id = false; - $this->data = array(); - $this->validationErrors = array(); - - if ($data !== null && $data !== false) { - foreach ($this->schema() as $field => $properties) { - if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') { - $defaults[$field] = $properties['default']; - } - } - $this->set($defaults); - $this->set($data); - } - if ($filterKey) { - $this->set($this->primaryKey, false); - } - return $this->data; - } - -/** - * Returns a list of fields from the database, and sets the current model - * data (Model::$data) with the record found. - * - * @param mixed $fields String of single fieldname, or an array of fieldnames. - * @param mixed $id The ID of the record to read - * @return array Array of database fields, or false if not found - * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-read - */ - public function read($fields = null, $id = null) { - $this->validationErrors = array(); - - if ($id != null) { - $this->id = $id; - } - - $id = $this->id; - - if (is_array($this->id)) { - $id = $this->id[0]; - } - - if ($id !== null && $id !== false) { - $this->data = $this->find('first', array( - 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), - 'fields' => $fields - )); - return $this->data; - } else { - return false; - } - } - -/** - * Returns the contents of a single field given the supplied conditions, in the - * supplied order. - * - * @param string $name Name of field to get - * @param array $conditions SQL conditions (defaults to NULL) - * @param string $order SQL ORDER BY fragment - * @return string field contents, or false if not found - * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-field - */ - public function field($name, $conditions = null, $order = null) { - if ($conditions === null && $this->id !== false) { - $conditions = array($this->alias . '.' . $this->primaryKey => $this->id); - } - if ($this->recursive >= 1) { - $recursive = -1; - } else { - $recursive = $this->recursive; - } - $fields = $name; - if ($data = $this->find('first', compact('conditions', 'fields', 'order', 'recursive'))) { - if (strpos($name, '.') === false) { - if (isset($data[$this->alias][$name])) { - return $data[$this->alias][$name]; - } - } else { - $name = explode('.', $name); - if (isset($data[$name[0]][$name[1]])) { - return $data[$name[0]][$name[1]]; - } - } - if (isset($data[0]) && count($data[0]) > 0) { - return array_shift($data[0]); - } - } else { - return false; - } - } - -/** - * Saves the value of a single field to the database, based on the current - * model ID. - * - * @param string $name Name of the table field - * @param mixed $value Value of the field - * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed - * @return boolean See Model::save() - * @see Model::save() - * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-read - */ - public function saveField($name, $value, $validate = false) { - $id = $this->id; - $this->create(false); - - if (is_array($validate)) { - $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate); - } else { - $options = array('validate' => $validate, 'fieldList' => array($name)); - } - return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options); - } - -/** - * Saves model data (based on white-list, if supplied) to the database. By - * default, validation occurs before save. - * - * @param array $data Data to save. - * @param mixed $validate Either a boolean, or an array. - * If a boolean, indicates whether or not to validate before saving. - * If an array, allows control of validate, callbacks, and fieldList - * @param array $fieldList List of fields to allow to be written - * @return mixed On success Model::$data if its not empty or true, false on failure - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html - */ - public function save($data = null, $validate = true, $fieldList = array()) { - $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true); - $_whitelist = $this->whitelist; - $fields = array(); - - if (!is_array($validate)) { - $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks')); - } else { - $options = array_merge($defaults, $validate); - } - - if (!empty($options['fieldList'])) { - $this->whitelist = $options['fieldList']; - } elseif ($options['fieldList'] === null) { - $this->whitelist = array(); - } - $this->set($data); - - if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) { - return false; - } - - foreach (array('created', 'updated', 'modified') as $field) { - $keyPresentAndEmpty = ( - isset($this->data[$this->alias]) && - array_key_exists($field, $this->data[$this->alias]) && - $this->data[$this->alias][$field] === null - ); - if ($keyPresentAndEmpty) { - unset($this->data[$this->alias][$field]); - } - } - - $exists = $this->exists(); - $dateFields = array('modified', 'updated'); - - if (!$exists) { - $dateFields[] = 'created'; - } - if (isset($this->data[$this->alias])) { - $fields = array_keys($this->data[$this->alias]); - } - if ($options['validate'] && !$this->validates($options)) { - $this->whitelist = $_whitelist; - return false; - } - - $db = $this->getDataSource(); - - foreach ($dateFields as $updateCol) { - if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) { - $default = array('formatter' => 'date'); - $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]); - if (!array_key_exists('format', $colType)) { - $time = strtotime('now'); - } else { - $time = $colType['formatter']($colType['format']); - } - if (!empty($this->whitelist)) { - $this->whitelist[] = $updateCol; - } - $this->set($updateCol, $time); - } - } - - if ($options['callbacks'] === true || $options['callbacks'] === 'before') { - $result = $this->Behaviors->trigger('beforeSave', array(&$this, $options), array( - 'break' => true, 'breakOn' => array(false, null) - )); - if (!$result || !$this->beforeSave($options)) { - $this->whitelist = $_whitelist; - return false; - } - } - - if (empty($this->data[$this->alias][$this->primaryKey])) { - unset($this->data[$this->alias][$this->primaryKey]); - } - $fields = $values = array(); - - foreach ($this->data as $n => $v) { - if (isset($this->hasAndBelongsToMany[$n])) { - if (isset($v[$n])) { - $v = $v[$n]; - } - $joined[$n] = $v; - } else { - if ($n === $this->alias) { - foreach (array('created', 'updated', 'modified') as $field) { - if (array_key_exists($field, $v) && empty($v[$field])) { - unset($v[$field]); - } - } - - foreach ($v as $x => $y) { - if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) { - list($fields[], $values[]) = array($x, $y); - } - } - } - } - } - $count = count($fields); - - if (!$exists && $count > 0) { - $this->id = false; - } - $success = true; - $created = false; - - if ($count > 0) { - $cache = $this->_prepareUpdateFields(array_combine($fields, $values)); - - if (!empty($this->id)) { - $success = (bool)$db->update($this, $fields, $values); - } else { - $fInfo = $this->schema($this->primaryKey); - $isUUID = ($fInfo['length'] == 36 && - ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary') - ); - if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) { - if (array_key_exists($this->primaryKey, $this->data[$this->alias])) { - $j = array_search($this->primaryKey, $fields); - $values[$j] = String::uuid(); - } else { - list($fields[], $values[]) = array($this->primaryKey, String::uuid()); - } - } - - if (!$db->create($this, $fields, $values)) { - $success = $created = false; - } else { - $created = true; - } - } - - if ($success && !empty($this->belongsTo)) { - $this->updateCounterCache($cache, $created); - } - } - - if (!empty($joined) && $success === true) { - $this->_saveMulti($joined, $this->id, $db); - } - - if ($success && $count > 0) { - if (!empty($this->data)) { - $success = $this->data; - if ($created) { - $this->data[$this->alias][$this->primaryKey] = $this->id; - } - } - if ($options['callbacks'] === true || $options['callbacks'] === 'after') { - $this->Behaviors->trigger('afterSave', array(&$this, $created, $options)); - $this->afterSave($created); - } - if (!empty($this->data)) { - $success = Set::merge($success, $this->data); - } - $this->data = false; - $this->_clearCache(); - $this->validationErrors = array(); - } - $this->whitelist = $_whitelist; - return $success; - } - -/** - * Saves model hasAndBelongsToMany data to the database. - * - * @param array $joined Data to save - * @param mixed $id ID of record in this model - * @param DataSource $db - * @return void - */ - protected function _saveMulti($joined, $id, $db) { - foreach ($joined as $assoc => $data) { - - if (isset($this->hasAndBelongsToMany[$assoc])) { - list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); - - $keyInfo = $this->{$join}->schema($this->{$join}->primaryKey); - $isUUID = !empty($this->{$join}->primaryKey) && ( - $keyInfo['length'] == 36 && ( - $keyInfo['type'] === 'string' || - $keyInfo['type'] === 'binary' - ) - ); - - $newData = $newValues = array(); - $primaryAdded = false; - - $fields = array( - $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']), - $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']) - ); - - $idField = $db->name($this->{$join}->primaryKey); - if ($isUUID && !in_array($idField, $fields)) { - $fields[] = $idField; - $primaryAdded = true; - } - - foreach ((array)$data as $row) { - if (!empty($row) && (is_string($row) /*&& (strlen($row) == 36 || strlen($row) == 16)*/) || is_numeric($row)) { # QUICKAPPS MOD - $values = array($id, $row); - if ($isUUID && $primaryAdded) { - $values[] = String::uuid(); - } - $newValues[] = $values; - unset($values); - } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { - $newData[] = $row; - } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { - $newData[] = $row[$join]; - } - } - - if ($this->hasAndBelongsToMany[$assoc]['unique']) { - $conditions = array( - $join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id - ); - if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) { - $conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']); - } - $links = $this->{$join}->find('all', array( - 'conditions' => $conditions, - 'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0, - 'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey'] - )); - - $associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey']; - $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}"); - if (!empty($oldLinks)) { - $conditions[$associationForeignKey] = $oldLinks; - $db->delete($this->{$join}, $conditions); - } - } - - if (!empty($newData)) { - foreach ($newData as $data) { - $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id; - $this->{$join}->create($data); - $this->{$join}->save(); - } - } - - if (!empty($newValues)) { - $db->insertMulti($this->{$join}, $fields, $newValues); - } - } - } - } - -/** - * Updates the counter cache of belongsTo associations after a save or delete operation - * - * @param array $keys Optional foreign key data, defaults to the information $this->data - * @param boolean $created True if a new record was created, otherwise only associations with - * 'counterScope' defined get updated - * @return void - */ - public function updateCounterCache($keys = array(), $created = false) { - $keys = empty($keys) ? $this->data[$this->alias] : $keys; - $keys['old'] = isset($keys['old']) ? $keys['old'] : array(); - - foreach ($this->belongsTo as $parent => $assoc) { - if (!empty($assoc['counterCache'])) { - if (!is_array($assoc['counterCache'])) { - if (isset($assoc['counterScope'])) { - $assoc['counterCache'] = array($assoc['counterCache'] => $assoc['counterScope']); - } else { - $assoc['counterCache'] = array($assoc['counterCache'] => array()); - } - } - - $foreignKey = $assoc['foreignKey']; - $fkQuoted = $this->escapeField($assoc['foreignKey']); - - foreach ($assoc['counterCache'] as $field => $conditions) { - if (!is_string($field)) { - $field = Inflector::underscore($this->alias) . '_count'; - } - if (!$this->{$parent}->hasField($field)) { - continue; - } - if ($conditions === true) { - $conditions = array(); - } else { - $conditions = (array)$conditions; - } - - if (!array_key_exists($foreignKey, $keys)) { - $keys[$foreignKey] = $this->field($foreignKey); - } - $recursive = (empty($conditions) ? -1 : 0); - - if (isset($keys['old'][$foreignKey])) { - if ($keys['old'][$foreignKey] != $keys[$foreignKey]) { - $conditions[$fkQuoted] = $keys['old'][$foreignKey]; - $count = intval($this->find('count', compact('conditions', 'recursive'))); - - $this->{$parent}->updateAll( - array($field => $count), - array($this->{$parent}->escapeField() => $keys['old'][$foreignKey]) - ); - } - } - $conditions[$fkQuoted] = $keys[$foreignKey]; - - if ($recursive === 0) { - $conditions = array_merge($conditions, (array)$conditions); - } - $count = intval($this->find('count', compact('conditions', 'recursive'))); - - $this->{$parent}->updateAll( - array($field => $count), - array($this->{$parent}->escapeField() => $keys[$foreignKey]) - ); - } - } - } - } - -/** - * Helper method for Model::updateCounterCache(). Checks the fields to be updated for - * - * @param array $data The fields of the record that will be updated - * @return array Returns updated foreign key values, along with an 'old' key containing the old - * values, or empty if no foreign keys are updated. - */ - protected function _prepareUpdateFields($data) { - $foreignKeys = array(); - foreach ($this->belongsTo as $assoc => $info) { - if ($info['counterCache']) { - $foreignKeys[$assoc] = $info['foreignKey']; - } - } - $included = array_intersect($foreignKeys, array_keys($data)); - - if (empty($included) || empty($this->id)) { - return array(); - } - $old = $this->find('first', array( - 'conditions' => array($this->primaryKey => $this->id), - 'fields' => array_values($included), - 'recursive' => -1 - )); - return array_merge($data, array('old' => $old[$this->alias])); - } - -/** - * Backwards compatible passtrough method for: - * saveMany(), validateMany(), saveAssociated() and validateAssociated() - * - * Saves multiple individual records for a single model; Also works with a single record, as well as - * all its associated records. - * - * #### Options - * - * - validate: Set to false to disable validation, true to validate each record before saving, - * 'first' to validate *all* records before any are saved (default), - * or 'only' to only validate the records, but not save them. - * - atomic: If true (default), will attempt to save all records in a single transaction. - * Should be set to false if database/table does not support transactions. - * - fieldList: Equivalent to the $fieldList parameter in Model::save() - * - * @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple - * records of the same type), or an array indexed by association name. - * @param array $options Options to use when saving record data, See $options above. - * @return mixed If atomic: True on success, or false on failure. - * Otherwise: array similar to the $data array passed, but values are set to true/false - * depending on whether each record saved successfully. - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array - */ - public function saveAll($data = null, $options = array()) { - $options = array_merge(array('validate' => 'first'), $options); - if (Set::numeric(array_keys($data))) { - if ($options['validate'] === 'only') { - return $this->validateMany($data, $options); - } - return $this->saveMany($data, $options); - } - if ($options['validate'] === 'only') { - $validatesAssoc = $this->validateAssociated($data, $options); - if (isset($this->validationErrors[$this->alias]) && $this->validationErrors[$this->alias] === false) { - return false; - } - return $validatesAssoc; - } - return $this->saveAssociated($data, $options); - } - -/** - * Saves multiple individual records for a single model - * - * #### Options - * - * - validate: Set to false to disable validation, true to validate each record before saving, - * 'first' to validate *all* records before any are saved (default), - * - atomic: If true (default), will attempt to save all records in a single transaction. - * Should be set to false if database/table does not support transactions. - * - fieldList: Equivalent to the $fieldList parameter in Model::save() - * - * @param array $data Record data to save. This should be a numerically-indexed array - * @param array $options Options to use when saving record data, See $options above. - * @return mixed If atomic: True on success, or false on failure. - * Otherwise: array similar to the $data array passed, but values are set to true/false - * depending on whether each record saved successfully. - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savemany-array-data-null-array-options-array - */ - public function saveMany($data = null, $options = array()) { - if (empty($data)) { - $data = $this->data; - } - - $options = array_merge(array('validate' => 'first', 'atomic' => true), $options); - $this->validationErrors = $validationErrors = array(); - - if (empty($data) && $options['validate'] !== false) { - $result = $this->save($data, $options); - return !empty($result); - } - - if ($options['validate'] === 'first') { - $validates = $this->validateMany($data, $options); - if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { - return $validates; - } - } - - if ($options['atomic']) { - $db = $this->getDataSource(); - $transactionBegun = $db->begin($this); - } - $return = array(); - foreach ($data as $key => $record) { - $validates = ($this->create(null) !== null && $this->save($record, $options)); - if (!$validates) { - $validationErrors[$key] = $this->validationErrors; - } - if (!$options['atomic']) { - $return[] = $validates; - } elseif (!$validates) { - break; - } - } - $this->validationErrors = $validationErrors; - - if (!$options['atomic']) { - return $return; - } - if ($validates) { - if ($transactionBegun) { - return $db->commit($this) !== false; - } else { - return true; - } - } - $db->rollback($this); - return false; - } - -/** - * Validates multiple individual records for a single model - * - * #### Options - * - * - atomic: If true (default), returns boolean. If false returns array. - * - fieldList: Equivalent to the $fieldList parameter in Model::save() - * - * @param array $data Record data to validate. This should be a numerically-indexed array - * @param array $options Options to use when validating record data (see above), See also $options of validates(). - * @return boolean True on success, or false on failure. - * @return mixed If atomic: True on success, or false on failure. - * Otherwise: array similar to the $data array passed, but values are set to true/false - * depending on whether each record validated successfully. - */ - public function validateMany($data, $options = array()) { - $options = array_merge(array('atomic' => true), $options); - $this->validationErrors = $validationErrors = $return = array(); - foreach ($data as $key => $record) { - $validates = $this->create($record) && $this->validates($options); - if (!$validates) { - $validationErrors[$key] = $this->validationErrors; - } - $return[] = $validates; - } - $this->validationErrors = $validationErrors; - if (!$options['atomic']) { - return $return; - } - if (empty($this->validationErrors)) { - return true; - } - return false; - } - -/** - * Saves a single record, as well as all its directly associated records. - * - * #### Options - * - * - validate: Set to false to disable validation, true to validate each record before saving, - * 'first' to validate *all* records before any are saved (default), - * - atomic: If true (default), will attempt to save all records in a single transaction. - * Should be set to false if database/table does not support transactions. - * - fieldList: Equivalent to the $fieldList parameter in Model::save() - * - * @param array $data Record data to save. This should be an array indexed by association name. - * @param array $options Options to use when saving record data, See $options above. - * @return mixed If atomic: True on success, or false on failure. - * Otherwise: array similar to the $data array passed, but values are set to true/false - * depending on whether each record saved successfully. - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array - */ - public function saveAssociated($data = null, $options = array()) { - if (empty($data)) { - $data = $this->data; - } - - $options = array_merge(array('validate' => true, 'atomic' => true), $options); - $this->validationErrors = $validationErrors = array(); - - if (empty($data) && $options['validate'] !== false) { - $result = $this->save($data, $options); - return !empty($result); - } - - if ($options['validate'] === 'first') { - $validates = $this->validateAssociated($data, $options); - if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) { - return $validates; - } - } - if ($options['atomic']) { - $db = $this->getDataSource(); - $transactionBegun = $db->begin($this); - } - $associations = $this->getAssociated(); - $return = array(); - $validates = true; - foreach ($data as $association => $values) { - if (isset($associations[$association]) && $associations[$association] === 'belongsTo') { - if ($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options)) { - $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id; - } else { - $validationErrors[$association] = $this->{$association}->validationErrors; - $validates = false; - } - $return[$association][] = $validates; - } - } - if ($validates && !($this->create(null) !== null && $this->save($data, $options))) { - $validationErrors[$this->alias] = $this->validationErrors; - $validates = false; - } - $return[$this->alias] = $validates; - - foreach ($data as $association => $values) { - if (!$validates) { - break; - } - if (isset($associations[$association])) { - $type = $associations[$association]; - switch ($type) { - case 'hasOne': - $values[$this->{$type}[$association]['foreignKey']] = $this->id; - if (!($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options))) { - $validationErrors[$association] = $this->{$association}->validationErrors; - $validates = false; - } - $return[$association][] = $validates; - break; - case 'hasMany': - foreach ($values as $i => $value) { - $values[$i][$this->{$type}[$association]['foreignKey']] = $this->id; - } - $_return = $this->{$association}->saveMany($values, array_merge($options, array('atomic' => false))); - if (in_array(false, $_return, true)) { - $validationErrors[$association] = $this->{$association}->validationErrors; - $validates = false; - } - $return[$association] = $_return; - break; - } - } - } - $this->validationErrors = $validationErrors; - - if (isset($validationErrors[$this->alias])) { - $this->validationErrors = $validationErrors[$this->alias]; - } - - if (!$options['atomic']) { - return $return; - } - if ($validates) { - if ($transactionBegun) { - return $db->commit($this) !== false; - } else { - return true; - } - } - $db->rollback($this); - return false; - } - -/** - * Validates a single record, as well as all its directly associated records. - * - * #### Options - * - * - atomic: If true (default), returns boolean. If false returns array. - * - fieldList: Equivalent to the $fieldList parameter in Model::save() - * - * @param array $data Record data to validate. This should be an array indexed by association name. - * @param array $options Options to use when validating record data (see above), See also $options of validates(). - * @return array|boolean If atomic: True on success, or false on failure. - * Otherwise: array similar to the $data array passed, but values are set to true/false - * depending on whether each record validated successfully. - */ - public function validateAssociated($data, $options = array()) { - $options = array_merge(array('atomic' => true), $options); - $this->validationErrors = $validationErrors = $return = array(); - if (!($this->create($data) && $this->validates($options))) { - $validationErrors[$this->alias] = $this->validationErrors; - $return[$this->alias] = false; - } else { - $return[$this->alias] = true; - } - $associations = $this->getAssociated(); - $validates = true; - foreach ($data as $association => $values) { - if (isset($associations[$association])) { - if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { - $validates = $this->{$association}->create($values) && $this->{$association}->validates($options); - $return[$association][] = $validates; - } elseif($associations[$association] === 'hasMany') { - $validates = $this->{$association}->validateMany($values, $options); - $return[$association] = $validates; - } - if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $validationErrors[$association] = $this->{$association}->validationErrors; - } - } - } - - $this->validationErrors = $validationErrors; - if (isset($validationErrors[$this->alias])) { - $this->validationErrors = $validationErrors[$this->alias]; - } - if (!$options['atomic']) { - return $return; - } - if ($return[$this->alias] === false || !empty($this->validationErrors)) { - return false; - } - return true; - } - -/** - * Updates multiple model records based on a set of conditions. - * - * @param array $fields Set of fields and values, indexed by fields. - * Fields are treated as SQL snippets, to insert literal values manually escape your data. - * @param mixed $conditions Conditions to match, true for all records - * @return boolean True on success, false on failure - * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-array-conditions - */ - public function updateAll($fields, $conditions = true) { - return $this->getDataSource()->update($this, $fields, null, $conditions); - } - -/** - * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success. - * - * @param mixed $id ID of record to delete - * @param boolean $cascade Set to true to delete records that depend on this record - * @return boolean True on success - * @link http://book.cakephp.org/2.0/en/models/deleting-data.html - */ - public function delete($id = null, $cascade = true) { - if (!empty($id)) { - $this->id = $id; - } - $id = $this->id; - - if ($this->beforeDelete($cascade)) { - $filters = $this->Behaviors->trigger( - 'beforeDelete', - array(&$this, $cascade), - array('break' => true, 'breakOn' => array(false, null)) - ); - if (!$filters || !$this->exists()) { - return false; - } - $db = $this->getDataSource(); - - $this->_deleteDependent($id, $cascade); - $this->_deleteLinks($id); - $this->id = $id; - - $updateCounterCache = false; - if (!empty($this->belongsTo)) { - foreach ($this->belongsTo as $parent => $assoc) { - if (!empty($assoc['counterCache'])) { - $updateCounterCache = true; - break; - } - } - - $keys = $this->find('first', array( - 'fields' => $this->_collectForeignKeys(), - 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), - 'recursive' => -1, - 'callbacks' => false - )); - } - - if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) { - if ($updateCounterCache) { - $this->updateCounterCache($keys[$this->alias]); - } - $this->Behaviors->trigger('afterDelete', array(&$this)); - $this->afterDelete(); - $this->_clearCache(); - $this->id = false; - return true; - } - } - return false; - } - -/** - * Cascades model deletes through associated hasMany and hasOne child records. - * - * @param string $id ID of record that was deleted - * @param boolean $cascade Set to true to delete records that depend on this record - * @return void - */ - protected function _deleteDependent($id, $cascade) { - if (!empty($this->__backAssociation)) { - $savedAssociatons = $this->__backAssociation; - $this->__backAssociation = array(); - } - foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) { - if ($data['dependent'] === true && $cascade === true) { - - $model = $this->{$assoc}; - $conditions = array($model->escapeField($data['foreignKey']) => $id); - if ($data['conditions']) { - $conditions = array_merge((array)$data['conditions'], $conditions); - } - $model->recursive = -1; - - if (isset($data['exclusive']) && $data['exclusive']) { - $model->deleteAll($conditions); - } else { - $records = $model->find('all', array( - 'conditions' => $conditions, 'fields' => $model->primaryKey - )); - - if (!empty($records)) { - foreach ($records as $record) { - $model->delete($record[$model->alias][$model->primaryKey]); - } - } - } - } - } - if (isset($savedAssociatons)) { - $this->__backAssociation = $savedAssociatons; - } - } - -/** - * Cascades model deletes through HABTM join keys. - * - * @param string $id ID of record that was deleted - * @return void - */ - protected function _deleteLinks($id) { - foreach ($this->hasAndBelongsToMany as $assoc => $data) { - list($plugin, $joinModel) = pluginSplit($data['with']); - $records = $this->{$joinModel}->find('all', array( - 'conditions' => array($this->{$joinModel}->escapeField($data['foreignKey']) => $id), - 'fields' => $this->{$joinModel}->primaryKey, - 'recursive' => -1, - 'callbacks' => false - )); - if (!empty($records)) { - foreach ($records as $record) { - $this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->primaryKey]); - } - } - } - } - -/** - * Deletes multiple model records based on a set of conditions. - * - * @param mixed $conditions Conditions to match - * @param boolean $cascade Set to true to delete records that depend on this record - * @param boolean $callbacks Run callbacks - * @return boolean True on success, false on failure - * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall - */ - public function deleteAll($conditions, $cascade = true, $callbacks = false) { - if (empty($conditions)) { - return false; - } - $db = $this->getDataSource(); - - if (!$cascade && !$callbacks) { - return $db->delete($this, $conditions); - } else { - $ids = $this->find('all', array_merge(array( - 'fields' => "{$this->alias}.{$this->primaryKey}", - 'recursive' => 0), compact('conditions')) - ); - if ($ids === false) { - return false; - } - - $ids = Set::extract($ids, "{n}.{$this->alias}.{$this->primaryKey}"); - if (empty($ids)) { - return true; - } - - if ($callbacks) { - $_id = $this->id; - $result = true; - foreach ($ids as $id) { - $result = ($result && $this->delete($id, $cascade)); - } - $this->id = $_id; - return $result; - } else { - foreach ($ids as $id) { - $this->_deleteLinks($id); - if ($cascade) { - $this->_deleteDependent($id, $cascade); - } - } - return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids)); - } - } - } - -/** - * Collects foreign keys from associations. - * - * @param string $type - * @return array - */ - protected function _collectForeignKeys($type = 'belongsTo') { - $result = array(); - - foreach ($this->{$type} as $assoc => $data) { - if (isset($data['foreignKey']) && is_string($data['foreignKey'])) { - $result[$assoc] = $data['foreignKey']; - } - } - return $result; - } - -/** - * Returns true if a record with the currently set ID exists. - * - * Internally calls Model::getID() to obtain the current record ID to verify, - * and then performs a Model::find('count') on the currently configured datasource - * to ascertain the existence of the record in persistent storage. - * - * @return boolean True if such a record exists - */ - public function exists() { - if ($this->getID() === false) { - return false; - } - $conditions = array($this->alias . '.' . $this->primaryKey => $this->getID()); - $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false); - return ($this->find('count', $query) > 0); - } - -/** - * Returns true if a record that meets given conditions exists. - * - * @param array $conditions SQL conditions array - * @return boolean True if such a record exists - */ - public function hasAny($conditions = null) { - return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false); - } - -/** - * Queries the datasource and returns a result set array. - * - * Also used to perform notation finds, where the first argument is type of find operation to perform - * (all / first / count / neighbors / list / threaded ), - * second parameter options for finding ( indexed array, including: 'conditions', 'limit', - * 'recursive', 'page', 'fields', 'offset', 'order') - * - * Eg: - * {{{ - * find('all', array( - * 'conditions' => array('name' => 'Thomas Anderson'), - * 'fields' => array('name', 'email'), - * 'order' => 'field3 DESC', - * 'recursive' => 2, - * 'group' => 'type' - * )); - * }}} - * - * In addition to the standard query keys above, you can provide Datasource, and behavior specific - * keys. For example, when using a SQL based datasource you can use the joins key to specify additional - * joins that should be part of the query. - * - * {{{ - * find('all', array( - * 'conditions' => array('name' => 'Thomas Anderson'), - * 'joins' => array( - * array( - * 'alias' => 'Thought', - * 'table' => 'thoughts', - * 'type' => 'LEFT', - * 'conditions' => '`Thought`.`person_id` = `Person`.`id`' - * ) - * ) - * )); - * }}} - * - * Behaviors and find types can also define custom finder keys which are passed into find(). - * - * Specifying 'fields' for notation 'list': - * - * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value. - * - If a single field is specified, 'id' is used for key and specified field is used for value. - * - If three fields are specified, they are used (in order) for key, value and group. - * - Otherwise, first and second fields are used for key and value. - * - * Note: find(list) + database views have issues with MySQL 5.0. Try upgrading to MySQL 5.1 if you - * have issues with database views. - * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) - * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) - * @return array Array of records - * @link http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall - */ - public function find($type = 'first', $query = array()) { - $this->findQueryType = $type; - $this->id = $this->getID(); - - $query = $this->buildQuery($type, $query); - if (is_null($query)) { - return null; - } - - $results = $this->getDataSource()->read($this, $query); - $this->resetAssociations(); - - if ($query['callbacks'] === true || $query['callbacks'] === 'after') { - $results = $this->_filterResults($results); - } - - $this->findQueryType = null; - - if ($type === 'all') { - return $results; - } else { - if ($this->findMethods[$type] === true) { - return $this->{'_find' . ucfirst($type)}('after', $query, $results); - } - } - } - -/** - * Builds the query array that is used by the data source to generate the query to fetch the data. - * - * @param string $type Type of find operation (all / first / count / neighbors / list / threaded) - * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks) - * @return array Query array or null if it could not be build for some reasons - * @see Model::find() - */ - public function buildQuery($type = 'first', $query = array()) { - $query = array_merge( - array( - 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null, - 'offset' => null, 'order' => null, 'page' => 1, 'group' => null, 'callbacks' => true, - ), - (array)$query - ); - - if ($type !== 'all') { - if ($this->findMethods[$type] === true) { - $query = $this->{'_find' . ucfirst($type)}('before', $query); - } - } - - if (!is_numeric($query['page']) || intval($query['page']) < 1) { - $query['page'] = 1; - } - if ($query['page'] > 1 && !empty($query['limit'])) { - $query['offset'] = ($query['page'] - 1) * $query['limit']; - } - if ($query['order'] === null && $this->order !== null) { - $query['order'] = $this->order; - } - $query['order'] = array($query['order']); - - if ($query['callbacks'] === true || $query['callbacks'] === 'before') { - $return = $this->Behaviors->trigger( - 'beforeFind', - array(&$this, $query), - array('break' => true, 'breakOn' => array(false, null), 'modParams' => 1) - ); - - $query = (is_array($return)) ? $return : $query; - - if ($return === false) { - return null; - } - - $return = $this->beforeFind($query); - $query = (is_array($return)) ? $return : $query; - - if ($return === false) { - return null; - } - } - - return $query; - } - -/** - * Handles the before/after filter logic for find('first') operations. Only called by Model::find(). - * - * @param string $state Either "before" or "after" - * @param array $query - * @param array $results - * @return array - * @see Model::find() - */ - protected function _findFirst($state, $query, $results = array()) { - if ($state === 'before') { - $query['limit'] = 1; - return $query; - } elseif ($state === 'after') { - if (empty($results[0])) { - return false; - } - return $results[0]; - } - } - -/** - * Handles the before/after filter logic for find('count') operations. Only called by Model::find(). - * - * @param string $state Either "before" or "after" - * @param array $query - * @param array $results - * @return integer The number of records found, or false - * @see Model::find() - */ - protected function _findCount($state, $query, $results = array()) { - if ($state === 'before') { - $db = $this->getDataSource(); - if (empty($query['fields'])) { - $query['fields'] = $db->calculate($this, 'count'); - } elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) { - $query['fields'] = $db->calculate($this, 'count', array( - $db->expression($query['fields']), 'count' - )); - } - $query['order'] = false; - return $query; - } elseif ($state === 'after') { - foreach (array(0, $this->alias) as $key) { - if (isset($results[0][$key]['count'])) { - if (($count = count($results)) > 1) { - return $count; - } else { - return intval($results[0][$key]['count']); - } - } - } - return false; - } - } - -/** - * Handles the before/after filter logic for find('list') operations. Only called by Model::find(). - * - * @param string $state Either "before" or "after" - * @param array $query - * @param array $results - * @return array Key/value pairs of primary keys/display field values of all records found - * @see Model::find() - */ - protected function _findList($state, $query, $results = array()) { - if ($state === 'before') { - if (empty($query['fields'])) { - $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}"); - $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null); - } else { - if (!is_array($query['fields'])) { - $query['fields'] = String::tokenize($query['fields']); - } - - if (count($query['fields']) === 1) { - if (strpos($query['fields'][0], '.') === false) { - $query['fields'][0] = $this->alias . '.' . $query['fields'][0]; - } - - $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null); - $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]); - } elseif (count($query['fields']) === 3) { - for ($i = 0; $i < 3; $i++) { - if (strpos($query['fields'][$i], '.') === false) { - $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; - } - } - - $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]); - } else { - for ($i = 0; $i < 2; $i++) { - if (strpos($query['fields'][$i], '.') === false) { - $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; - } - } - - $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null); - } - } - if (!isset($query['recursive']) || $query['recursive'] === null) { - $query['recursive'] = -1; - } - list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list; - return $query; - } elseif ($state === 'after') { - if (empty($results)) { - return array(); - } - $lst = $query['list']; - return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']); - } - } - -/** - * Detects the previous field's value, then uses logic to find the 'wrapping' - * rows and return them. - * - * @param string $state Either "before" or "after" - * @param mixed $query - * @param array $results - * @return array - */ - protected function _findNeighbors($state, $query, $results = array()) { - if ($state === 'before') { - extract($query); - $conditions = (array)$conditions; - if (isset($field) && isset($value)) { - if (strpos($field, '.') === false) { - $field = $this->alias . '.' . $field; - } - } else { - $field = $this->alias . '.' . $this->primaryKey; - $value = $this->id; - } - $query['conditions'] = array_merge($conditions, array($field . ' <' => $value)); - $query['order'] = $field . ' DESC'; - $query['limit'] = 1; - $query['field'] = $field; - $query['value'] = $value; - return $query; - } elseif ($state === 'after') { - extract($query); - unset($query['conditions'][$field . ' <']); - $return = array(); - if (isset($results[0])) { - $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]); - $query['conditions'][$field . ' >='] = $prevVal[0]; - $query['conditions'][$field . ' !='] = $value; - $query['limit'] = 2; - } else { - $return['prev'] = null; - $query['conditions'][$field . ' >'] = $value; - $query['limit'] = 1; - } - $query['order'] = $field . ' ASC'; - $return2 = $this->find('all', $query); - if (!array_key_exists('prev', $return)) { - $return['prev'] = $return2[0]; - } - if (count($return2) === 2) { - $return['next'] = $return2[1]; - } elseif (count($return2) === 1 && !$return['prev']) { - $return['next'] = $return2[0]; - } else { - $return['next'] = null; - } - return $return; - } - } - -/** - * In the event of ambiguous results returned (multiple top level results, with different parent_ids) - * top level results with different parent_ids to the first result will be dropped - * - * @param mixed $state - * @param mixed $query - * @param array $results - * @return array Threaded results - */ - protected function _findThreaded($state, $query, $results = array()) { - if ($state === 'before') { - return $query; - } elseif ($state === 'after') { - $return = $idMap = array(); - $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey); - - foreach ($results as $result) { - $result['children'] = array(); - $id = $result[$this->alias][$this->primaryKey]; - $parentId = $result[$this->alias]['parent_id']; - if (isset($idMap[$id]['children'])) { - $idMap[$id] = array_merge($result, (array)$idMap[$id]); - } else { - $idMap[$id] = array_merge($result, array('children' => array())); - } - if (!$parentId || !in_array($parentId, $ids)) { - $return[] =& $idMap[$id]; - } else { - $idMap[$parentId]['children'][] =& $idMap[$id]; - } - } - if (count($return) > 1) { - $ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return)); - if (count($ids) > 1) { - $root = $return[0][$this->alias]['parent_id']; - foreach ($return as $key => $value) { - if ($value[$this->alias]['parent_id'] != $root) { - unset($return[$key]); - } - } - } - } - return $return; - } - } - -/** - * Passes query results through model and behavior afterFilter() methods. - * - * @param array $results Results to filter - * @param boolean $primary If this is the primary model results (results from model where the find operation was performed) - * @return array Set of filtered results - */ - protected function _filterResults($results, $primary = true) { - $return = $this->Behaviors->trigger( - 'afterFind', - array(&$this, $results, $primary), - array('modParams' => 1) - ); - if ($return !== true) { - $results = $return; - } - return $this->afterFind($results, $primary); - } - -/** - * This resets the association arrays for the model back - * to those originally defined in the model. Normally called at the end - * of each call to Model::find() - * - * @return boolean Success - */ - public function resetAssociations() { - if (!empty($this->__backAssociation)) { - foreach ($this->_associations as $type) { - if (isset($this->__backAssociation[$type])) { - $this->{$type} = $this->__backAssociation[$type]; - } - } - $this->__backAssociation = array(); - } - - foreach ($this->_associations as $type) { - foreach ($this->{$type} as $key => $name) { - if (property_exists($this, $key) && !empty($this->{$key}->__backAssociation)) { - $this->{$key}->resetAssociations(); - } - } - } - $this->__backAssociation = array(); - return true; - } - -/** - * Returns false if any fields passed match any (by default, all if $or = false) of their matching values. - * - * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data) - * @param boolean $or If false, all fields specified must match in order for a false return value - * @return boolean False if any records matching any fields are found - */ - public function isUnique($fields, $or = true) { - if (!is_array($fields)) { - $fields = func_get_args(); - if (is_bool($fields[count($fields) - 1])) { - $or = $fields[count($fields) - 1]; - unset($fields[count($fields) - 1]); - } - } - - foreach ($fields as $field => $value) { - if (is_numeric($field)) { - unset($fields[$field]); - - $field = $value; - if (isset($this->data[$this->alias][$field])) { - $value = $this->data[$this->alias][$field]; - } else { - $value = null; - } - } - - if (strpos($field, '.') === false) { - unset($fields[$field]); - $fields[$this->alias . '.' . $field] = $value; - } - } - if ($or) { - $fields = array('or' => $fields); - } - if (!empty($this->id)) { - $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id; - } - return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0); - } - -/** - * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method. - * - * @param string $sql,... SQL statement - * @return array Resultset - * @link http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query - */ - public function query($sql) { - $params = func_get_args(); - $db = $this->getDataSource(); - return call_user_func_array(array(&$db, 'query'), $params); - } - -/** - * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations - * that use the 'with' key as well. Since _saveMulti is incapable of exiting a save operation. - * - * Will validate the currently set data. Use Model::set() or Model::create() to set the active data. - * - * @param string $options An optional array of custom options to be made available in the beforeValidate callback - * @return boolean True if there are no errors - */ - public function validates($options = array()) { - $errors = $this->invalidFields($options); - if (empty($errors) && $errors !== false) { - $errors = $this->_validateWithModels($options); - } - if (is_array($errors)) { - return count($errors) === 0; - } - return $errors; - } - -/** - * Returns an array of fields that have failed validation. On the current model. - * - * @param string $options An optional array of custom options to be made available in the beforeValidate callback - * @return array Array of invalid fields - * @see Model::validates() - */ - public function invalidFields($options = array()) { - if ( - !$this->Behaviors->trigger( - 'beforeValidate', - array(&$this, $options), - array('break' => true, 'breakOn' => false) - ) || - $this->beforeValidate($options) === false - ) { - return false; - } - - if (!isset($this->validate) || empty($this->validate)) { - return $this->validationErrors; - } - - $data = $this->data; - $methods = array_map('strtolower', get_class_methods($this)); - $behaviorMethods = array_keys($this->Behaviors->methods()); - - if (isset($data[$this->alias])) { - $data = $data[$this->alias]; - } elseif (!is_array($data)) { - $data = array(); - } - - $exists = $this->exists(); - - $_validate = $this->validate; - $whitelist = $this->whitelist; - - if (!empty($options['fieldList'])) { - $whitelist = $options['fieldList']; - } - - if (!empty($whitelist)) { - $validate = array(); - foreach ((array)$whitelist as $f) { - if (!empty($this->validate[$f])) { - $validate[$f] = $this->validate[$f]; - } - } - $this->validate = $validate; - } - - $validationDomain = $this->validationDomain; - if (empty($validationDomain)) { - $validationDomain = 'default'; - } - - foreach ($this->validate as $fieldName => $ruleSet) { - if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { - $ruleSet = array($ruleSet); - } - $default = array( - 'allowEmpty' => null, - 'required' => null, - 'rule' => 'blank', - 'last' => true, - 'on' => null - ); - - foreach ($ruleSet as $index => $validator) { - if (!is_array($validator)) { - $validator = array('rule' => $validator); - } - $validator = array_merge($default, $validator); - - if ( - empty($validator['on']) || ($validator['on'] == 'create' && - !$exists) || ($validator['on'] == 'update' && $exists - )) { - $valid = true; - $requiredFail = ( - (!isset($data[$fieldName]) && $validator['required'] === true) || - ( - isset($data[$fieldName]) && (empty($data[$fieldName]) && - !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false - ) - ); - - if (!$requiredFail && array_key_exists($fieldName, $data)) { - if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) { - break; - } - if (is_array($validator['rule'])) { - $rule = $validator['rule'][0]; - unset($validator['rule'][0]); - $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule'])); - } else { - $rule = $validator['rule']; - $ruleParams = array($data[$fieldName]); - } - - if (in_array(strtolower($rule), $methods)) { - $ruleParams[] = $validator; - $ruleParams[0] = array($fieldName => $ruleParams[0]); - $valid = $this->dispatchMethod($rule, $ruleParams); - } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { - $ruleParams[] = $validator; - $ruleParams[0] = array($fieldName => $ruleParams[0]); - $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); - } elseif (method_exists('Validation', $rule)) { - $valid = call_user_func_array(array('Validation', $rule), $ruleParams); - } elseif (!is_array($validator['rule'])) { - $valid = preg_match($rule, $data[$fieldName]); - } elseif (Configure::read('debug') > 0) { - trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING); - } - } - - if ($requiredFail || !$valid || (is_string($valid) && strlen($valid) > 0)) { - if (is_string($valid)) { - $message = $valid; - } elseif (isset($validator['message'])) { - $args = null; - if (is_array($validator['message'])) { - $message = $validator['message'][0]; - $args = array_slice($validator['message'], 1); - } else { - $message = $validator['message']; - } - if (is_array($validator['rule']) && $args === null) { - $args = array_slice($ruleSet[$index]['rule'], 1); - } - $message = __d($validationDomain, $message, $args); - } elseif (is_string($index)) { - if (is_array($validator['rule'])) { - $args = array_slice($ruleSet[$index]['rule'], 1); - $message = __d($validationDomain, $index, $args); - } else { - $message = __d($validationDomain, $index); - } - } elseif (!$requiredFail && is_numeric($index) && count($ruleSet) > 1) { - $message = $index + 1; - } else { - $message = __d('cake_dev', 'This field cannot be left blank'); - } - - $this->invalidate($fieldName, $message); - if ($validator['last']) { - break; - } - } - } - } - } - $this->validate = $_validate; - return $this->validationErrors; - } - -/** - * Runs validation for hasAndBelongsToMany associations that have 'with' keys - * set. And data in the set() data set. - * - * @param array $options Array of options to use on Valdation of with models - * @return boolean Failure of validation on with models. - * @see Model::validates() - */ - protected function _validateWithModels($options) { - $valid = true; - foreach ($this->hasAndBelongsToMany as $assoc => $association) { - if (empty($association['with']) || !isset($this->data[$assoc])) { - continue; - } - list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); - $data = $this->data[$assoc]; - - $newData = array(); - foreach ((array)$data as $row) { - if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { - $newData[] = $row; - } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { - $newData[] = $row[$join]; - } - } - if (empty($newData)) { - continue; - } - foreach ($newData as $data) { - $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id; - $this->{$join}->create($data); - $valid = ($valid && $this->{$join}->validates($options)); - } - } - return $valid; - } -/** - * Marks a field as invalid, optionally setting the name of validation - * rule (in case of multiple validation for field) that was broken. - * - * @param string $field The name of the field to invalidate - * @param mixed $value Name of validation rule that was not failed, or validation message to - * be returned. If no validation key is provided, defaults to true. - * @return void - */ - public function invalidate($field, $value = true) { - if (!is_array($this->validationErrors)) { - $this->validationErrors = array(); - } - $this->validationErrors = Set::insert($this->validationErrors, $field, $value); # QUICKAPPS MOD - } - -/** - * Returns true if given field name is a foreign key in this model. - * - * @param string $field Returns true if the input string ends in "_id" - * @return boolean True if the field is a foreign key listed in the belongsTo array. - */ - public function isForeignKey($field) { - $foreignKeys = array(); - if (!empty($this->belongsTo)) { - foreach ($this->belongsTo as $assoc => $data) { - $foreignKeys[] = $data['foreignKey']; - } - } - return in_array($field, $foreignKeys); - } - -/** - * Escapes the field name and prepends the model name. Escaping is done according to the - * current database driver's rules. - * - * @param string $field Field to escape (e.g: id) - * @param string $alias Alias for the model (e.g: Post) - * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`). - */ - public function escapeField($field = null, $alias = null) { - if (empty($alias)) { - $alias = $this->alias; - } - if (empty($field)) { - $field = $this->primaryKey; - } - $db = $this->getDataSource(); - if (strpos($field, $db->name($alias) . '.') === 0) { - return $field; - } - return $db->name($alias . '.' . $field); - } - -/** - * Returns the current record's ID - * - * @param integer $list Index on which the composed ID is located - * @return mixed The ID of the current record, false if no ID - */ - public function getID($list = 0) { - if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) { - return false; - } - - if (!is_array($this->id)) { - return $this->id; - } - - if (empty($this->id)) { - return false; - } - - if (isset($this->id[$list]) && !empty($this->id[$list])) { - return $this->id[$list]; - } elseif (isset($this->id[$list])) { - return false; - } - - foreach ($this->id as $id) { - return $id; - } - - return false; - } - -/** - * Returns the ID of the last record this model inserted. - * - * @return mixed Last inserted ID - */ - public function getLastInsertID() { - return $this->getInsertID(); - } - -/** - * Returns the ID of the last record this model inserted. - * - * @return mixed Last inserted ID - */ - public function getInsertID() { - return $this->_insertID; - } - -/** - * Sets the ID of the last record this model inserted - * - * @param mixed $id Last inserted ID - * @return void - */ - public function setInsertID($id) { - $this->_insertID = $id; - } - -/** - * Returns the number of rows returned from the last query. - * - * @return integer Number of rows - */ - public function getNumRows() { - return $this->getDataSource()->lastNumRows(); - } - -/** - * Returns the number of rows affected by the last query. - * - * @return integer Number of rows - */ - public function getAffectedRows() { - return $this->getDataSource()->lastAffected(); - } - -/** - * Sets the DataSource to which this model is bound. - * - * @param string $dataSource The name of the DataSource, as defined in app/Config/database.php - * @return boolean True on success - * @throws MissingConnectionException - */ - public function setDataSource($dataSource = null) { - $oldConfig = $this->useDbConfig; - - if ($dataSource != null) { - $this->useDbConfig = $dataSource; - } - $db = ConnectionManager::getDataSource($this->useDbConfig); - if (!empty($oldConfig) && isset($db->config['prefix'])) { - $oldDb = ConnectionManager::getDataSource($oldConfig); - - if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) { - $this->tablePrefix = $db->config['prefix']; - } - } elseif (isset($db->config['prefix'])) { - $this->tablePrefix = $db->config['prefix']; - } - - if (empty($db) || !is_object($db)) { - throw new MissingConnectionException(array('class' => $this->name)); - } - } - -/** - * Gets the DataSource to which this model is bound. - * - * @return DataSource A DataSource object - */ - public function getDataSource() { - if (!$this->_sourceConfigured && $this->useTable !== false) { - $this->_sourceConfigured = true; - $this->setSource($this->useTable); - } - return ConnectionManager::getDataSource($this->useDbConfig); - } - -/** - * Get associations - * - * @return array - */ - public function associations() { - return $this->_associations; - } - -/** - * Gets all the models with which this model is associated. - * - * @param string $type Only result associations of this type - * @return array Associations - */ - public function getAssociated($type = null) { - if ($type == null) { - $associated = array(); - foreach ($this->_associations as $assoc) { - if (!empty($this->{$assoc})) { - $models = array_keys($this->{$assoc}); - foreach ($models as $m) { - $associated[$m] = $assoc; - } - } - } - return $associated; - } elseif (in_array($type, $this->_associations)) { - if (empty($this->{$type})) { - return array(); - } - return array_keys($this->{$type}); - } else { - $assoc = array_merge( - $this->hasOne, - $this->hasMany, - $this->belongsTo, - $this->hasAndBelongsToMany - ); - if (array_key_exists($type, $assoc)) { - foreach ($this->_associations as $a) { - if (isset($this->{$a}[$type])) { - $assoc[$type]['association'] = $a; - break; - } - } - return $assoc[$type]; - } - return null; - } - } - -/** - * Gets the name and fields to be used by a join model. This allows specifying join fields - * in the association definition. - * - * @param string|array $assoc The model to be joined - * @param array $keys Any join keys which must be merged with the keys queried - * @return array - */ - public function joinModel($assoc, $keys = array()) { - if (is_string($assoc)) { - list(, $assoc) = pluginSplit($assoc); - return array($assoc, array_keys($this->{$assoc}->schema())); - } elseif (is_array($assoc)) { - $with = key($assoc); - return array($with, array_unique(array_merge($assoc[$with], $keys))); - } - trigger_error( - __d('cake_dev', 'Invalid join model settings in %s', $model->alias), - E_USER_WARNING - ); - } - -/** - * Called before each find operation. Return false if you want to halt the find - * call, otherwise return the (modified) query data. - * - * @param array $queryData Data used to execute this query, i.e. conditions, order, etc. - * @return mixed true if the operation should continue, false if it should abort; or, modified - * $queryData to continue with new $queryData - * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeFind-1049 - */ - public function beforeFind($queryData) { - return true; - } - -/** - * Called after each find operation. Can be used to modify any results returned by find(). - * Return value should be the (modified) results. - * - * @param mixed $results The results of the find operation - * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association) - * @return mixed Result of the find operation - * @link http://book.cakephp.org/view/1048/Callback-Methods#afterFind-1050 - */ - public function afterFind($results, $primary = false) { - return $results; - } - -/** - * Called before each save operation, after validation. Return a non-true result - * to halt the save. - * - * @param array $options - * @return boolean True if the operation should continue, false if it should abort - * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeSave-1052 - */ - public function beforeSave($options = array()) { - return true; - } - -/** - * Called after each successful save operation. - * - * @param boolean $created True if this save created a new record - * @return void - * @link http://book.cakephp.org/view/1048/Callback-Methods#afterSave-1053 - */ - public function afterSave($created) { - } - -/** - * Called before every deletion operation. - * - * @param boolean $cascade If true records that depend on this record will also be deleted - * @return boolean True if the operation should continue, false if it should abort - * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeDelete-1054 - */ - public function beforeDelete($cascade = true) { - return true; - } - -/** - * Called after every deletion operation. - * - * @return void - * @link http://book.cakephp.org/view/1048/Callback-Methods#afterDelete-1055 - */ - public function afterDelete() { - } - -/** - * Called during validation operations, before validation. Please note that custom - * validation rules can be defined in $validate. - * - * @param array $options Options passed from model::save(), see $options of model::save(). - * @return boolean True if validate operation should continue, false to abort - * @link http://book.cakephp.org/view/1048/Callback-Methods#beforeValidate-1051 - */ - public function beforeValidate($options = array()) { - return true; - } - -/** - * Called when a DataSource-level error occurs. - * - * @return void - * @link http://book.cakephp.org/view/1048/Callback-Methods#onError-1056 - */ - public function onError() { - } - -/** - * Clears cache for this model. - * - * @param string $type If null this deletes cached views if Cache.check is true - * Will be used to allow deleting query cache also - * @return boolean true on delete - * @todo - */ - protected function _clearCache($type = null) { - if ($type === null) { - if (Configure::read('Cache.check') === true) { - $assoc[] = strtolower(Inflector::pluralize($this->alias)); - $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias))); - foreach ($this->_associations as $key => $association) { - foreach ($this->$association as $key => $className) { - $check = strtolower(Inflector::pluralize($className['className'])); - if (!in_array($check, $assoc)) { - $assoc[] = strtolower(Inflector::pluralize($className['className'])); - $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className']))); - } - } - } - clearCache($assoc); - return true; - } - } else { - //Will use for query cache deleting - } - } -} diff --git a/app/Locale/spa/LC_MESSAGES/core.po b/app/Locale/spa/LC_MESSAGES/core.po deleted file mode 100644 index d706aee1..00000000 --- a/app/Locale/spa/LC_MESSAGES/core.po +++ /dev/null @@ -1,64 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-01-01 00:00+0100\n" -"PO-Revision-Date: \n" -"Last-Translator: Christopher Castro \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Country: SPAIN\n" -"X-Poedit-KeywordsList: __t;__\n" -"X-Poedit-Basepath: ../../../\n" -"X-Poedit-SearchPath-0: .\n" - -msgid "Dashboard" -msgstr "Resumen" - -msgid "Structure" -msgstr "Estructura" - -msgid "Content" -msgstr "Contenidos" - -msgid "Appearance" -msgstr "Apariencia" - -msgid "Modules" -msgstr "Módulos" - -msgid "Users" -msgstr "Usuarios" - -msgid "Configuration" -msgstr "Configuración" - -msgid "Help" -msgstr "Ayuda" - -msgid "Hidden" -msgstr "Oculto" - -msgid "Languages" -msgstr "Idiomas" - -msgid "View Site" -msgstr "Ver Sitio" - -msgid "Log out" -msgstr "Cerrar sesión" - -msgid "Logout" -msgstr "Cerrar sesión" - -msgid "My account" -msgstr "Mi cuenta" - -msgid "« Previous " -msgstr "« Anterior " - -msgid " Next »" -msgstr " Siguiente »" - diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php deleted file mode 100644 index d8b2b106..00000000 --- a/app/Model/AppModel.php +++ /dev/null @@ -1,205 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class AppModel extends Model { - public $cacheQueries = false; - public $listeners = array(); - public $events = array(); - public $actsAs = array( - 'WhoDidIt' => array( - 'auth_session' => 'Auth.User.id', - 'user_model' => 'User.User' - ) - ); - public $Options = array( - 'break' => false, - 'breakOn' => false, - 'collectReturn' => false - ); - private $__Options = array( - 'break' => false, - 'breakOn' => false, - 'collectReturn' => false - ); - - public function __construct($id = false, $table = null, $ds = null) { - $this->__loadHooks(); - parent::__construct($id, $table, $ds); - $this->__loadHookEvents(); - } - -/** - * Marks a field as invalid, optionally setting the name of validation - * rule (in case of multiple validation for field) that was broken. - * - * @param string $field The name of the field to invalidate - * @param mixed $value Name of validation rule that was not failed, or validation message to - * be returned. If no validation key is provided, defaults to true. - * @return void - */ - public function invalidate($field, $value = true) { - $value = is_string($value) ? __t($value) : $value; - parent::invalidate($field, $value); - - return; - } - -/** - * Chech if hook exists - * - * @param string $hook Name of the hook to check - * @return bool - */ - public function hook_defined($hook) { - return (in_array($hook, $this->events) == true); - } - -/** - * Trigger a callback method on every HookBehavior. - * - * ### Options - * - * - `breakOn` Set to the value or values you want the callback propagation to stop on. - * Can either be a scalar value, or an array of values to break on. - * Defaults to `false`. - * - * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value - * will be returned. If used in combination with `collectReturn` the collected results will be returned. - * Defaults to `false`. - * - * - `collectReturn` Set to true to collect the return of each object into an array. - * This array of return values will be returned from the hook() call. Defaults to `false`. - * - * - `alter` Allows each callback gets called on to modify the parameters to the next object. - * Defaults to true. - * - * @param string $event name of the hook to call - * @param mixed $data data for the triggered callback - * @param array $option Array of options - * @return mixed Either the last result or all results if collectReturn is on. Or null in case of no response - */ - public function hook($hook, &$data = array(), $options = array()) { - return $this->__dispatchEvent($hook, $data, $options); - } - -/** - * Overwrite default options for Hook dispatcher. - * Useful when calling a hook with non-parameter and custom options. - * - * Watch out!: Hook dispatcher automatic reset its default options to - * the original ones after `hook()` is invoked. - * Means that if you need to call more than one hook (consecutive) with no parameters and - * same options ** you must call `setHookOptions()` after each hook() ** - * - * ### Usage - * For example in any controller action: - * {{{ - * $this->setHookOptions(array('collectReturn' => false)); - * $response = $this->hook('collect_hook_with_no_parameters'); - * - * $this->setHookOptions(array('collectReturn' => false, 'break' => true, 'breakOn' => false)); - * $response2 = $this->hook('OTHER_collect_hook_with_no_parameters'); - * }}} - * - * @param array $options Array of options to overwrite - * @return void - */ - public function setHookOptions($options) { - $this->Options = Set::merge($this->Options, $options); - } - -/** - * Dispatch Component-hooks from all the plugins and core - * - * @see AppModel::hook() - * @return mixed Either the last result or all results if collectReturn is on. Or NULL in case of no response - */ - private function __dispatchEvent($event, &$data = array(), $options = array()) { - $options = array_merge($this->Options, (array)$options); - $collected = array(); - - if (!$this->hook_defined($event)) { - $this->__resetOptions(); - - return null; - } - - foreach ($this->listeners as $object => $methods) { - foreach ($methods as $method) { - if ($method == $event && is_callable(array($this->Behaviors->{$object}, $method))) { - $result = @call_user_func(array($this->Behaviors->{$object}, $event), $data); - - if ($options['collectReturn'] === true) { - $collected[] = $result; - } - - if ($options['break'] && - ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true))) - ) { - $this->__resetOptions(); - - return $result; - } - } - } - } - - if (empty($collected) && in_array($result, array('', null), true)) { - $this->__resetOptions(); - - return null; - } - - $this->__resetOptions(); - - return $options['collectReturn'] ? $collected : $result; - } - - private function __resetOptions() { - if ($this->Options !== $this->__Options) { - $this->Options = $this->__Options; - } - } - - private function __loadHooks() { - $b = Configure::read('Hook.behaviors'); - - if (!$b){ - return false; # fix for AppController __preloadHooks() - } - - foreach ($b as $hook) { - $this->actsAs[$hook] = array(); - } - } - - private function __loadHookEvents() { - foreach ($this->actsAs as $behavior => $b_data) { - $behavior = strpos($behavior, '.') !== false ? substr($behavior, strpos($behavior, '.')+1) : $behavior; - - if (strpos($behavior, 'Hook')) { - $methods = array(); - $_methods = get_this_class_methods($this->Behaviors->{$behavior}); - - foreach ($_methods as $method) { - $methods[] = $method; - } - - $this->listeners[$behavior] = $methods; - $this->events = array_merge($this->events, $methods); - } - } - - $this->events = array_unique($this->events); - - return true; - } -} \ No newline at end of file diff --git a/app/Model/Behavior/SerializedBehavior.php b/app/Model/Behavior/SerializedBehavior.php deleted file mode 100644 index 514a017d..00000000 --- a/app/Model/Behavior/SerializedBehavior.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class SerializedBehavior extends ModelBehavior { - -/** - * Fields - * - * @var array - * @access protected - */ - private $fields = array(); - -/** - * Initiate Serialized behavior - * - * @param object $Model instance of model - * @param array $config array of configuration settings. - * @return void - * @access public - */ - public function setup($Model, $config = array()) { - if (is_string($config)) { - $config = array($config); - } - - $this->fields = array_merge($this->fields, $config); - } - - public function afterFind(&$Model, $results, $primary) { - $_results = $results; - - if (isset($_results[0][$Model->alias])) { - foreach ($_results as $rkey => &$record) { - foreach ($this->fields as $field) { - if (isset($record[$Model->alias][$field]) && - !empty($record[$Model->alias][$field]) && - is_string($record[$Model->alias][$field]) - ) { - $record[$Model->alias][$field] = @unserialize($record[$Model->alias][$field]); - } - } - } - } else { - foreach ($this->fields as $field) { - if (isset($_results[$Model->alias][$field]) && - !empty($_results[$Model->alias][$field]) && - is_string($_results[$Model->alias][$field]) - ) { - $_results[$Model->alias][$field] = @unserialize($_results[$Model->alias][$field]); - } - } - } - - return $_results; - } - - public function beforeSave($Model) { - if (isset($Model->data[$Model->alias][0])) { - foreach ($Model->data[$Model->alias] as &$record) { - foreach ($record as $field => &$data) { - if (!in_array($field, $this->fields)) { - continue; - } - - $data = $this->serialize($data); - } - } - } elseif (isset($Model->data[0])) { - foreach ($Model->data as $key => &$row) { - foreach ($row as $field => &$value) { - if (!in_array($field, $this->fields)) { - continue; - } - - $value = $this->serialize($value); - } - } - } else { - foreach ($Model->data[$Model->alias] as $field => &$data) { - if (!in_array($field, $this->fields)) { - continue; - } - - $data = $this->serialize($data); - } - } - - return true; - } - - public function serialize($data) { - return (empty($data) ? @serialize(array()): @serialize($data)); - } -} \ No newline at end of file diff --git a/app/Model/Behavior/SluggableBehavior.php b/app/Model/Behavior/SluggableBehavior.php deleted file mode 100644 index 2e2b8cb4..00000000 --- a/app/Model/Behavior/SluggableBehavior.php +++ /dev/null @@ -1,195 +0,0 @@ - array('title'), 'slug' => 'slug', 'separator' => '-', 'length' => 200, 'overwrite' => true, 'translation' => null); - - if (!isset($this->__settings[$Model->alias])) - { - $this->__settings[$Model->alias] = $default; - } - - $this->__settings[$Model->alias] = am($this->__settings[$Model->alias], (is_array($settings) ? $settings : array())); - } - - /** - * Run before a model is saved, used to set up slug for model. - * - * @param object $Model Model about to be saved. - * @return boolean true if save should proceed, false otherwise - * @access public - */ - function beforeSave(&$Model) - { - $return = parent::beforeSave($Model); - - // Make label fields an array - - if (!is_array($this->__settings[$Model->alias]['label'])) - { - $this->__settings[$Model->alias]['label'] = array($this->__settings[$Model->alias]['label']); - } - - // Make sure all label fields are available - - foreach ($this->__settings[$Model->alias]['label'] as $field) - { - if (!$Model->hasField($field)) - { - return $return; - } - } - - // See if we should be generating a slug - if ($Model->hasField($this->__settings[$Model->alias]['slug']) && ($this->__settings[$Model->alias]['overwrite'] || empty($Model->id))) - { - // Build label out of data in label fields, if available, or using a default slug otherwise - - $label = ''; - foreach ($this->__settings[$Model->alias]['label'] as $field) - { - if (!empty($Model->data[$Model->alias][$field])) - { - $label .= (!empty($label) ? ' ' : '') . $Model->data[$Model->alias][$field]; - } - } - - // Keep on going only if we've got something to slug - - if (!empty($label)) - { - // Get the slug - - $slug = $this->__slug($label, $this->__settings[$Model->alias]); - - // Look for slugs that start with the same slug we've just generated - // Bug 1 - // The following line is not working any more: - // $conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'] => 'LIKE ' . $slug . '%'); - - // Fix for Bug1: - //$conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'] => $slug); // Fix 1 - $conditions = array($Model->alias . '.' . $this->__settings[$Model->alias]['slug'].' LIKE' => $slug.'%'); // Fix 2 - - if (!empty($Model->id)) - { - // Bug 2 - // The following line is not working any more: - // $conditions[$Model->alias . '.' . $Model->primaryKey] = '!= ' . $Model->id; - - // Fix for Bug 2: - $conditions['not'] = array( - $Model->alias . '.' . $Model->primaryKey => - $Model->id - ); - } - $result = $Model->find('all', array('conditions' => $conditions, 'fields' => array($Model->primaryKey, $this->__settings[$Model->alias]['slug']), 'recursive' => -1)); - $sameUrls = null; - - if (!empty($result)) - { - $sameUrls = Set::extract($result, '{n}.' . $Model->alias . '.' . $this->__settings[$Model->alias]['slug']); - } - - // If we have collissions - - if (!empty($sameUrls)) - { - $begginingSlug = $slug; - $index = 1; - - // Attach an ending incremental number until we find a free slug - - while($index > 0) - { - if (!in_array($begginingSlug . $this->__settings[$Model->alias]['separator'] . $index, $sameUrls)) - { - $slug = $begginingSlug . $this->__settings[$Model->alias]['separator'] . $index; - $index = -1; - } - - $index++; - } - } - - // Now set the slug as part of the model data to be saved, making sure that - // we are on the white list of fields to be saved - - if (!empty($Model->whitelist) && !in_array($this->__settings[$Model->alias]['slug'], $Model->whitelist)) - { - $Model->whitelist[] = $this->__settings[$Model->alias]['slug']; - } - - $Model->data[$Model->alias][$this->__settings[$Model->alias]['slug']] = $slug; - } - } - - return $return; - } - - /** - * Generate a slug for the given string using specified settings. - * - * @param string $string String from where to generate slug - * @param array $settings Settings to use (looks for 'separator' and 'length') - * @return string Slug for given string - * @access private - */ - function __slug($string, $settings) { - $string = Inflector::slug($string, $settings['separator']); - $string = strtolower($string); - if (strlen($string) > $settings['length']) - $string = substr($string, 0, $settings['length']); - return $string; - } -} \ No newline at end of file diff --git a/app/Model/Behavior/WhoDidItBehavior.php b/app/Model/Behavior/WhoDidItBehavior.php deleted file mode 100644 index c6a563a4..00000000 --- a/app/Model/Behavior/WhoDidItBehavior.php +++ /dev/null @@ -1,99 +0,0 @@ - 'Auth', //name of Auth session key - 'user_model' => 'User', //name of User model - 'created_by_field' => 'created_by', //the name of the "created_by" field in DB (default 'created_by') - 'modified_by_field' => 'modified_by', //the name of the "modified_by" field in DB (default 'modified_by') - 'auto_bind' => true //automatically bind the model to the User model (default true) - ); - -/** - * Initiate WhoMadeIt Behavior - * - * @param object $model - * @param array $config behavior settings you would like to override - * @return void - * @access public - */ - function setup(&$model, $config = array()) { - //assigne default settings - $this->settings[$model->alias] = $this->_defaults; - - //merge custom config with default settings - $this->settings[$model->alias] = array_merge($this->settings[$model->alias], (array)$config); - - $hasFieldCreatedBy = $model->hasField($this->settings[$model->alias]['created_by_field']); - $hasFieldModifiedBy = $model->hasField($this->settings[$model->alias]['modified_by_field']); - - $this->settings[$model->alias]['has_created_by'] = $hasFieldCreatedBy; - $this->settings[$model->alias]['has_modified_by'] = $hasFieldModifiedBy; - - //handles model binding to the User model - //according to the auto_bind settings (default true) - if ($this->settings[$model->alias]['auto_bind']) - { - if ($hasFieldCreatedBy) { - $commonBelongsTo = array( - 'CreatedBy' => array('className' => $this->settings[$model->alias]['user_model'], - 'foreignKey' => $this->settings[$model->alias]['created_by_field']) - ); - $model->bindModel(array('belongsTo' => $commonBelongsTo), false); - } - - if ($hasFieldModifiedBy) { - $commonBelongsTo = array( - 'ModifiedBy' => array('className' => $this->settings[$model->alias]['user_model'], - 'foreignKey' => $this->settings[$model->alias]['modified_by_field'])); - $model->bindModel(array('belongsTo' => $commonBelongsTo), false); - } - } - } - -/** - * Before save callback - * - * @param object $model Model using this behavior - * @return boolean True if the operation should continue, false if it should abort - * @access public - */ - function beforeSave(&$model) { - if ($this->settings[$model->alias]['has_created_by'] || $this->settings[$model->alias]['has_modified_by']) { - $AuthSession = $this->settings[$model->alias]['auth_session']; - - App::uses('CakeSession', 'Model/Datasource'); - $userId = CakeSession::read($AuthSession); - if ($userId) { - $data = array($this->settings[$model->alias]['modified_by_field'] => $userId); - if (!$model->exists()) { - $data[$this->settings[$model->alias]['created_by_field']] = $userId; - } - $model->set($data); - } - } - return true; - } -} \ No newline at end of file diff --git a/app/Model/Datasource/empty b/app/Model/Datasource/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/Config/bootstrap.php b/app/Plugin/block/Config/bootstrap.php deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/Config/routes.php b/app/Plugin/block/Config/routes.php deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/Controller/BlockAppController.php b/app/Plugin/block/Controller/BlockAppController.php deleted file mode 100644 index c6872cd9..00000000 --- a/app/Plugin/block/Controller/BlockAppController.php +++ /dev/null @@ -1,14 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockAppController extends AppController { - -} \ No newline at end of file diff --git a/app/Plugin/block/Controller/BlockController.php b/app/Plugin/block/Controller/BlockController.php deleted file mode 100644 index 20ac143d..00000000 --- a/app/Plugin/block/Controller/BlockController.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockController extends BlockAppController { - public $name = 'Block'; - public $uses = array('Block.Block'); - - public function admin_index() { - $this->redirect('/admin/block/manage'); - } -} \ No newline at end of file diff --git a/app/Plugin/block/Controller/Component/BlockHookComponent.php b/app/Plugin/block/Controller/Component/BlockHookComponent.php deleted file mode 100644 index 6e6748e8..00000000 --- a/app/Plugin/block/Controller/Component/BlockHookComponent.php +++ /dev/null @@ -1,32 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockHookComponent extends Component { - public $Controller = null; - public $components = array('Hook'); - - public function initialize(&$Controller) { - $this->Controller = $Controller; - } - - public function blocks_list($params = array()) { - $params = array_merge($params, array('recursive' => 2)); - $Block = (isset($this->Controller->Block) && is_object($this->Controller->Block)) ? $this->Controller->Block : ClassRegistry::init('Block.Block'); - - $Block->Menu->unbindModel( - array('hasMany' => array('Block')) - ); - - $blocks = $Block->find('all', $params); - - return $blocks; - } -} \ No newline at end of file diff --git a/app/Plugin/block/Controller/ManageController.php b/app/Plugin/block/Controller/ManageController.php deleted file mode 100644 index 9b1c6387..00000000 --- a/app/Plugin/block/Controller/ManageController.php +++ /dev/null @@ -1,214 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class ManageController extends BlockAppController { - public $name = 'Manage'; - public $uses = array('Block.Block', 'User.Role'); - - public function admin_index() { - $site_theme = $this->Block->find('all', - array( - 'conditions' => array( - 'OR' => array( - 'Block.themes_cache LIKE' => '%:' . Configure::read('Variable.site_theme') . ":%" - ) - ) - ) - ); - - $admin_theme = $this->Block->find('all', - array( - 'conditions' => array( - 'OR' => array( - 'Block.themes_cache LIKE' => '%:' . Configure::read('Variable.admin_theme') . ":%", - ) - ) - ) - ); - - $site_ids = (array)Set::extract('/Block/id', $site_theme); - $admin_ids = (array)Set::extract('/Block/id', $admin_theme); - - $unassigned = $this->Block->find('all', - array( - 'conditions' => array( - 'NOT' => array( - 'Block.id' => array_merge($site_ids, $admin_ids) - ) - ) - ) - ); - - $this->set('site_theme', (array)$site_theme); - $this->set('admin_theme', (array)$admin_theme); - $this->set('unassigned', (array)$unassigned); - $this->title(__t('Blocks')); - $this->set('themes', $this->__themesYaml()); - $this->setCrumb('/admin/block'); - } - - public function admin_move($block_region_id, $dir) { - if (in_array($dir, array('up', 'down'))) { - if ($dir == 'up') { - $this->Block->BlockRegion->move($block_region_id, 'up'); - } else { - $this->Block->BlockRegion->move($block_region_id, 'down'); - } - } - - $this->redirect($this->referer()); - } - - public function admin_clone($bid) { - $block = $this->Block->findById($bid) or $this->redirect($this->referer()); - $block = Set::filter($block); - $block['Block']['themes_cache'] = ''; - $block['Block']['title'] .= ' (' . __t('Clone') . ')'; - $block['Block']['clone_of'] = $block['Block']['id']; - - unset($block['Block']['id'], $block['BlockRegion']); - - if ($this->Block->saveAll($block)) { - $this->flashMsg(__t('Block has been cloned'), 'success'); - $this->redirect('/admin/block/manage/edit/' . $this->Block->id); - } else { - $this->flashMsg(__t('Block could not be cloned'), 'error'); - } - - $this->redirect($this->referer()); - } - - public function admin_edit($bid) { - if (isset($this->data['Block'])) { - $data = $this->data; - $data['Block']['locale'] = !empty($data['Block']['locale']) ? array_values($data['Block']['locale']) : array(); - $data['Block']['themes_cache'] = $this->__themesCache($data['BlockRegion']); - - if ($this->Block->saveAll($data, array('validate' => 'first'))) { # saveAll only will save Block related models! - if (isset($data['Module'])) { # save widgets variables - $this->Module->save($data['Module']); - Cache::delete('Modules'); - $this->Quickapps->loadModules(); - } - - if (isset($data['Variable'])) { - $this->Variable->save($data['Variable']); - Cache::delete('Variable'); - $this->Quickapps->loadVariables(); - } - - $this->flashMsg(__t('Block has been saved'), 'success'); - } else { - $this->flashMsg(__t('Block could not be saved. Please, try again.'), 'error'); - } - - $this->redirect("/admin/block/manage/edit/{$bid}"); - } - - $themes = $this->__themesYaml(); - - foreach ($themes as $theme => $yaml) { - $_regions["{$yaml['info']['name']}@|@{$theme}"] = array(); - - foreach ($yaml['regions'] as $name => $title) { - $_regions["{$yaml['info']['name']}@|@{$theme}"]["{$name}"] = $title; - } - } - - $this->data = $this->Block->findById($bid) or $this->redirect('/admin/block/manage'); - - $this->title(__t('Editing Block')); - $this->setCrumb('/admin/block'); - $this->set('regions', $_regions); - $this->set('roles', $this->Role->find('list')); - } - - public function admin_add() { - $this->title(__t('Add new block')); - $this->setCrumb('/admin/block'); - $this->setCrumb(array(__t('New block'), '')); - - if (isset($this->data['Block'])) { - $data = $this->data; - - foreach ($data['BlockRegion'] as $key => $br) { - if (empty($br['region'])) { - unset($data['BlockRegion'][$key]); - } - } - - $data['Block']['module'] = 'block'; - $data['Block']['locale'] = !empty($data['Block']['locale']) ? array_values($data['Block']['locale']) : array(); - $data['Block']['themes_cache'] = $this->__themesCache($data['BlockRegion']); - - if ($this->Block->saveAll($data, array('validate' => 'first'))) { - $this->Block->BlockRegion->deleteAll( array('region' => '')); - $this->flashMsg(__t('Block has been saved'), 'success'); - $this->redirect("/admin/block/manage/edit/{$this->Block->id}"); - } else { - $this->flashMsg(__t('Block could not be saved. Please, try again.'), 'error'); - } - } - - $themes = $this->__themesYaml(); - - foreach ($themes as $theme => $yaml) { - $_regions["{$yaml['info']['name']}@|@{$theme}"] = array(); - - foreach ($yaml['regions'] as $name => $title) { - $_regions["{$yaml['info']['name']}@|@{$theme}"]["{$name}"] = $title; - } - } - - $this->set('regions', $_regions); - $this->set('roles', $this->Role->find('list')); - } - - public function admin_delete($id) { - $block = $this->Block->findById($id); - - if (!$block || ($block['Block']['module'] != 'block' && !$block['Block']['module'])) { - $this->redirect('/admin'); - } else { - $this->Block->delete($id); - $this->redirect($this->referer()); - } - } - - private function __themesCache($BlockRegion) { - $o = array(); - - foreach ($BlockRegion as $key => $r) { - if (!empty($r['region'])) { - $o[] = $r['theme']; - } - } - $o = ':' . implode(":", array_unique($o)) . ':'; - - return preg_replace('/\:{2,}/', ':', $o); - } - - private function __themesYaml() { - $return = array(); - $folder = new Folder; - $folder->path = APP . 'View' . DS . 'Themed'; - $folders = $folder->read(); - - foreach ($folders[0] as $theme) { - if (APP . 'View' . DS . 'Themed' . DS . $theme . DS . "{$theme}.yaml") { - $yaml = Spyc::YAMLLoad(APP . 'View' . DS . 'Themed' . DS . $theme . DS . "{$theme}.yaml"); - $return[$theme] = $yaml; - } - } - - return $return; - } -} \ No newline at end of file diff --git a/app/Plugin/block/Lib/empty b/app/Plugin/block/Lib/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po b/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po deleted file mode 100644 index 64f81fbf..00000000 --- a/app/Plugin/block/Locale/spa/LC_MESSAGES/core.po +++ /dev/null @@ -1,136 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-01-01 00:00+0100\n" -"PO-Revision-Date: \n" -"Last-Translator: Christopher Castro \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Country: SPAIN\n" -"X-Poedit-KeywordsList: __t;__\n" -"X-Poedit-Basepath: ../../../\n" -"X-Poedit-SearchPath-0: .\n" - -msgid "New Block" -msgstr "Nuevo Bloque" - -msgid "Save block" -msgstr "Guardar bloque" - -msgid "Unassigned" -msgstr "No Asignados" - -msgid "Blocks" -msgstr "Bloques" - -msgid "clone" -msgstr "clonar" - -msgid "configure" -msgstr "configurar" - -msgid "delete" -msgstr "eliminar" - -msgid "move up" -msgstr "mover arriba" - -msgid "move down" -msgstr "mover abajo" - -msgid "Block" -msgstr "Bloque" - -msgid "Region" -msgstr "Región" - -msgid "Actions" -msgstr "Acciones" - -msgid "Duplicate this block?" -msgstr "Duplicar este bloque?" - -msgid "Active" -msgstr "Activo" - -msgid "Block title" -msgstr "Título de bloque" - -msgid "The title of the block as shown to the user." -msgstr "Título de bloque que se mostrara al usuario." - -msgid "Block description *" -msgstr "Descripción de bloque *" - -msgid "A brief description of your block. Used on the Blocks administration page." -msgstr "Breve descripción del bloque. Utilizado en la sección de gestión de bloques." - -msgid "Block body *" -msgstr "Cuerpo de bloque *" - -msgid "Language" -msgstr "Idioma" - -msgid "Translations" -msgstr "Traducciones" - -msgid "Show this block for these languages" -msgstr "Mostrar este bloque para los siguientes idiomas" - -msgid "If no language is selected, block will show regardless of language." -msgstr "Si ningún idioma se indica, el bloque se mostrara independientemente del idioma." - -msgid "Visibility settings" -msgstr "Opciones de visualización" - -msgid "Theme Region" -msgstr "Región de Theme" - -msgid "Specify in which themes and regions this block is displayed." -msgstr "Indica en que theme y región este bloque se debe mostrar." - -msgid "Pages" -msgstr "Páginas" - -msgid "All pages except those listed" -msgstr "En todas las páginas excepto en las listadas" - -msgid "Only the listed pages" -msgstr "Sólo en las páginas listadas" - -msgid "Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are blog for the blog page and blog/* for every blog entry. '/' is the front page." -msgstr "Indique páginas usando su ruta. Ingrese una ruta por línea. El caracter '*' es un comodín. Ejemplos de rutas son blog para la página de blog y /blog/* para cualquier entrada de blog. '/' es la página principal." - -msgid "Roles" -msgstr "Roles" - -msgid "Show block for specific roles" -msgstr "Mostrar bloque para los roles indicados" - -msgid "Show this block only for the selected role(s). If you select no roles, the block will be visible to all users." -msgstr "Muestra este bloque solo para el(los) rol(es) seleccionado(s). Si no se indica(n) rol(es), el bloque será visible para todos los usuarios." - -msgid "Advanced Options" -msgstr "Opciones Avanzadas" - -msgid "Block class suffix" -msgstr "Sufijo de clase para bloque" - -msgid "A suffix to be applied to the CSS class of the block. This allows for individual block styling." -msgstr "Un sufijo para aplicar a las clases CSS del bloque. Esto permite estilizar individualmente el bloque." - -msgid "Editing Block" -msgstr "Editando Bloque" - -msgid "Add new block" -msgstr "Agregar nuevo bloque" - -msgid "Pages on which this PHP code returns TRUE (experts only)" -msgstr "Páginas en las que este código PHP retorne TRUE (sólo expertos)" - -msgid "If the PHP option is chosen, enter PHP code between <?php ?>. Note that executing incorrect PHP code can break your QuickApps site." -msgstr "Si se elige la opcion PHP, ingrese el código PHP entre <?php ?>. La ejecución de código PHP incorrecto puede romper su sitio QuickApps." - diff --git a/app/Plugin/block/Model/Behavior/BlockHookBehavior.php b/app/Plugin/block/Model/Behavior/BlockHookBehavior.php deleted file mode 100644 index 71eb9d48..00000000 --- a/app/Plugin/block/Model/Behavior/BlockHookBehavior.php +++ /dev/null @@ -1,14 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockHookBehavior extends ModelBehavior { - -} \ No newline at end of file diff --git a/app/Plugin/block/Model/Block.php b/app/Plugin/block/Model/Block.php deleted file mode 100644 index 9b165ee5..00000000 --- a/app/Plugin/block/Model/Block.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class Block extends BlockAppModel { - public $name = 'Block'; - public $useTable = 'blocks'; - public $primaryKey = 'id'; - public $actsAs = array('Serialized' => array('locale', 'settings', 'params')); - - public $hasOne = array( - 'BlockCustom' => array( - 'className' => 'Block.BlockCustom', - 'dependent' => true - ) - ); - - public $hasMany = array( - 'BlockRegion' => array( - 'className' => 'Block.BlockRegion', - 'dependent' => true - ) - ); - - public $belongsTo = array( - 'Menu' => array( - 'className' => 'Menu.Menu', - 'foreignKey' => 'delta', - 'conditions' => 'Block.module = "menu"', - 'dependent' => false - ) - ); - - public $hasAndBelongsToMany = array( - 'Role' => array( - 'joinTable' => 'block_roles', - 'className' => 'User.Role', - 'foreignKey' => 'block_id', - 'associationForeignKey' => 'user_role_id', - 'unique' => true, - 'dependent' => false - ) - ); - - public function beforeSave() { - /* get New delta */ - if (!isset($this->data['Block']['id'])) { # new record - if ($this->data['Block']['module'] == 'menu' || isset($this->data['Block']['delta'])) { - return true; - } - - $max_delta = $this->find('first', array('conditions' => array('Block.module' => 'block'), 'fields' => array('delta'), 'order' => array('delta' => 'DESC'))); - $max_delta = !empty($max_delta) ? $max_delta['Block']['delta'] + 1 : 1; - $this->data['Block']['delta'] = $max_delta; - } - - return true; - } -} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockAppModel.php b/app/Plugin/block/Model/BlockAppModel.php deleted file mode 100644 index b4fb1fcc..00000000 --- a/app/Plugin/block/Model/BlockAppModel.php +++ /dev/null @@ -1,14 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockAppModel extends AppModel { - -} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockCustom.php b/app/Plugin/block/Model/BlockCustom.php deleted file mode 100644 index b514d991..00000000 --- a/app/Plugin/block/Model/BlockCustom.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockCustom extends BlockAppModel { - public $name = 'BlockCustom'; - public $useTable = "block_custom"; - public $primaryKey = "block_id"; - public $validate = array( - 'description' => array('required' => true, 'allowEmpty' => false, 'rule' => 'notEmpty', 'message' => 'Invalid description'), - 'body' => array('required' => true, 'allowEmpty' => false, 'rule' => 'notEmpty', 'message' => 'Invalid block body'), - ); -} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockRegion.php b/app/Plugin/block/Model/BlockRegion.php deleted file mode 100644 index 5383794a..00000000 --- a/app/Plugin/block/Model/BlockRegion.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockRegion extends BlockAppModel { - public $name = 'BlockRegion'; - public $useTable = 'block_regions'; - public $order = array('BlockRegion.ordering' => 'ASC'); - public $primaryKey = 'id'; - - public function beforeSave() { - if (!isset($this->data['BlockRegion']['id'])) { - $r = $this->data['BlockRegion']['region']; - $t = $this->data['BlockRegion']['theme']; - $c = $this->find('count', array('conditions' => array('BlockRegion.theme' => $t, 'BlockRegion.region' => $r))); - $this->data['BlockRegion']['ordering'] = $c+1; - } - - return true; - } - - public function move($id, $dir = 'up') { - if (!$record = $this->findById($id)) { - return false; - } - - $nodes = $this->find('all', - array( - 'conditions' => array( - 'BlockRegion.theme' => $record['BlockRegion']['theme'], - 'BlockRegion.region' => $record['BlockRegion']['region'] - ), - 'order' => array('BlockRegion.ordering' => 'ASC'), - 'fields' => array('id', 'ordering'), - 'recursive' => -1 - ) - ); - $ids = Set::extract('/BlockRegion/id', $nodes); - - if (($dir == 'down' && $ids[count($ids)-1] == $record['BlockRegion']['id']) || - ($dir == 'up' && $ids[0] == $record['BlockRegion']['id']) - ) { #edge -> cant go down/up - return false; - } - - $position = array_search($record['BlockRegion']['id'], $ids); - $key = $dir == 'up' ? $position-1 : $position+1; - $tmp = $ids[$key]; - $ids[$key] = $ids[$position]; - $ids[$position] = $tmp; - - $i = 1; - - foreach ($ids as $id) { - $this->id = $id; - $this->saveField('ordering', $i, false); - $i++; - } - } - -} \ No newline at end of file diff --git a/app/Plugin/block/Model/BlockRole.php b/app/Plugin/block/Model/BlockRole.php deleted file mode 100644 index 221fe814..00000000 --- a/app/Plugin/block/Model/BlockRole.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockRole extends BlockAppModel { - public $name = 'BlockRole'; - public $useTable = "block_roles"; - public $primaryKey = null; -} \ No newline at end of file diff --git a/app/Plugin/block/View/Elements/help.ctp b/app/Plugin/block/View/Elements/help.ctp deleted file mode 100644 index 21ef2bdb..00000000 --- a/app/Plugin/block/View/Elements/help.ctp +++ /dev/null @@ -1,18 +0,0 @@ -

    About

    -

    - The Block module allows you to create boxes of content, which are rendered into an area, or region, of one or more pages of a website. - The core Default administration theme, for example, implements the regions "Content", "Help", "Dashboard main", and "Dashboard sidebar", and a block may appear in any one of these regions. - The Blocks administration page provides an interface for assigning a block to a region, and for controlling the order of blocks within regions. -

    - -

    Uses

    -
    -
    Positioning content
    -
    When working with blocks, remember that all themes do not implement the same regions, or display regions in the same way. Blocks are positioned on a per-theme basis. Users with permissions can disable blocks. Disabled blocks are listed on the Blocks administration page, but are not displayed in any region.
    - -
    Controlling visibility
    -
    Blocks can be configured to be visible only on certain pages, only to users of certain roles. Some dynamic blocks, such as those generated by modules, will be displayed only on certain pages.
    - -
    Creating custom blocks
    -
    Users with permissions can add custom blocks, which are then listed on the Blocks administration page. Once created, custom blocks behave just like default and module-generated blocks.
    -
    \ No newline at end of file diff --git a/app/Plugin/block/View/Elements/toolbar.ctp b/app/Plugin/block/View/Elements/toolbar.ctp deleted file mode 100644 index 011dd23a..00000000 --- a/app/Plugin/block/View/Elements/toolbar.ctp +++ /dev/null @@ -1,6 +0,0 @@ -Layout->toolbar($links); \ No newline at end of file diff --git a/app/Plugin/block/View/Helper/BlockHookHelper.php b/app/Plugin/block/View/Helper/BlockHookHelper.php deleted file mode 100644 index 55172b0d..00000000 --- a/app/Plugin/block/View/Helper/BlockHookHelper.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class BlockHookHelper extends AppHelper { - // toolbar - public function beforeLayout($layoutFile) { - $show_on = (isset($this->request->params['plugin']) && $this->request->params['plugin'] == 'block' && $this->request->params['action'] != 'admin_add'); - $this->_View->Layout->blockPush(array('body' => $this->_View->element('toolbar')), 'toolbar', $show_on); - - return true; - } - - // hookTag, block rendering - public function block($options) { - extract($options); - - if (!isset($id)) { - return; - } - - if ($_block = Set::extract("/Block[id={$id}]/..", $this->_View->viewVars['Layout']['blocks'])) { - $block = $_block[0]; - } else { - $block = ClassRegistry::init('Block.Block')->findById($id); - } - - if (!$block ) { - return; - } - - $region = isset($region) ? $region : false; - $title = isset($title) ? int_val($title) : false; - - return $this->_View->Layout->block($block, array('title' => $title, 'region' => $region)); - } - - // hooktag - public function block_title($options) { - extract($options); - - if (!isset($id)) { - return; - } - - if ($_block = Set::extract("/Block[id={$id}]/..", $this->_View->viewVars['Layout']['blocks'])) { - $block = $_block[0]; - } else { - $block = ClassRegistry::init('Block.Block')->findById($id); - } - - if (!$block) { - return false; - } - - return $this->_View->Layout->hookTags($block['Block']['title']); - } -} \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_add.ctp b/app/Plugin/block/View/Manage/admin_add.ctp deleted file mode 100644 index 677b0110..00000000 --- a/app/Plugin/block/View/Manage/admin_add.ctp +++ /dev/null @@ -1,75 +0,0 @@ -Form->create('Block', array('url' => "/admin/block/manage/add")); ?> - - Html->useTag('fieldsetstart', __t('Content')); ?> - Form->hidden('status', array('value' => 1)); ?> - Form->input('Block.title', array('label' => __t('Block title'))); ?> - - - Form->input('BlockCustom.description', array('required' => 'required', 'label' => __t('Block description *'))); ?> - - - Form->input('BlockCustom.body', array('required' => 'required', 'label' => __t('Block body *'), 'class' => 'full', 'type' => 'textarea', 'after' => '')); ?> - Html->useTag('fieldsetend'); ?> - - - Html->useTag('fieldsetstart', __t('Language')); ?> - Html->useTag('fieldsetstart', __t('Translations')); ?> - - Form->input('locale', array('options' => $langs, 'type' => 'select', 'selected' => Set::extract('/Block/locale', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show this block for these languages'))); ?> - - Html->useTag('fieldsetend'); ?> - Html->useTag('fieldsetend'); ?> - - - Html->useTag('fieldsetstart', __t('Visibility settings')); ?> - Html->useTag('fieldsetstart', __t('Theme Region')); ?> -
    - $_regions ): ?> - - - Form->select("BlockRegion.{$i}.region", $_regions, array('empty' => __t('--None--'))) . "\n"; ?> - Form->hidden("BlockRegion.{$i}.theme", array('value' => $theme[1])) . "\n"; ?> - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Pages')); ?> - Form->input('visibility', - array( - 'type' => 'radio', - 'legend' => false, - 'value' => 0, - 'separator' => '
    ', - 'options' => array( - 0 => __t('All pages except those listed'), - 1 => __t('Only the listed pages'), - 2 => __t('Pages on which this PHP code returns TRUE (experts only)') - ) - ) - ); - ?> - - Form->input('pages', array('type' => 'textarea', 'label' => false, 'after' => '')); ?> - - - - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Roles')); ?> - Form->input('Role', array('options' => $roles, 'type' => 'select', 'multiple' => 'checkbox', 'label' => __t('Show block for specific roles'))); ?> - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Advanced Options')); ?> - Form->input('Block.params.class', array('label' => __t('Block class suffix'))); ?> - - Html->useTag('fieldsetend'); ?> - Html->useTag('fieldsetend'); ?> - - - Form->input(__t('Save block'), array('type' => 'submit')); ?> -Form->end(); ?> \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_edit.ctp b/app/Plugin/block/View/Manage/admin_edit.ctp deleted file mode 100644 index fd1fc625..00000000 --- a/app/Plugin/block/View/Manage/admin_edit.ctp +++ /dev/null @@ -1,98 +0,0 @@ -Form->create('Block', array('url' => "/admin/block/manage/edit/{$this->data['Block']['id']}")); ?> - - Html->useTag('fieldsetstart', __t('Content')); ?> - Form->hidden('id'); ?> - Form->input('status', array('type' => 'checkbox', 'label' => __t('Active'))) . "\n"; ?> - Form->input('title', array('label' => __t('Block title'))); ?> - - - data['Block']['module'] === 'block'): # custom data only for custom blocks ?> - Form->input('BlockCustom.description', array('required' => 'required', 'label' => __t('Block description *'))); ?> - - Form->input('BlockCustom.body', array('required' => 'required', 'type' => 'textarea', 'class' => 'full', 'label' => __t('Block body *'), 'after' => '')); ?> - - Html->useTag('fieldsetend'); ?> - - data['Block']['module'] !== 'block'): ?> - Layout->attachModuleHooks($this->data['Block']['module']); ?> - data; ?> - Layout->hook("{$this->data['Block']['module']}_{$this->data['Block']['delta']}_settings", $data, array('collectReturn' => false))): # widget ?> - Html->useTag('fieldsetstart', 'Widget settings'); ?> - - Html->useTag('fieldsetend'); ?> - - Layout->deattachModuleHooks(Inflector::camelize($this->data['Block']['module'])); ?> - - - - Html->useTag('fieldsetstart', __t('Language')); ?> - Html->useTag('fieldsetstart', __t('Translations')); ?> - - Form->input('locale', array('options' => $langs, 'type' => 'select', 'selected' => Set::extract('/Block/locale', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show this block for these languages'))); ?> - - Html->useTag('fieldsetend'); ?> - Html->useTag('fieldsetend'); ?> - - - Html->useTag('fieldsetstart', __t('Visibility settings')); ?> - Html->useTag('fieldsetstart', __t('Theme Region')); ?> -
    - $_regions ): ?> - - - data); - $selected = !empty($selected) && isset($selected[0]) ? $selected[0] : null; - ?> - - Form->select("BlockRegion.{$i}.region", $_regions, array('value' => $selected, 'empty' => __t('--None--'))) . "\n"; ?> - Form->hidden("BlockRegion.{$i}.theme", array('value' => $theme[1])) . "\n"; ?> - Form->hidden("BlockRegion.{$i}.block_id", array('value' => $this->data['Block']['id'])) . "\n"; ?> - - - data); ?> - Form->hidden("BlockRegion.{$i}.id", array('value' => $brId[0])) . "\n"; ?> - - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Pages')); ?> - Form->input('visibility', - array( - 'type' => 'radio', - 'legend' => false, - 'separator' => '
    ', - 'options' => array( - 0 => __t('All pages except those listed'), - 1 => __t('Only the listed pages'), - 2 => __t('Pages on which this PHP code returns TRUE (experts only)') - ) - ) - ); - ?> - - Form->input('pages', array('type' => 'textarea', 'class' => 'plain', 'label' => false)); ?> - - - - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Roles')); ?> - Form->input('Role', array('options' => $roles, 'type' => 'select', 'selected' => Set::extract('/Role/id', $this->data), 'multiple' => 'checkbox', 'label' => __t('Show block for specific roles'))); ?> - - Html->useTag('fieldsetend'); ?> - - Html->useTag('fieldsetstart', __t('Advanced Options')); ?> - Form->input('Block.params.class', array('value' => (isset($this->data['Block']['params']['class']) ? $this->data['Block']['params']['class'] : ''), 'label' => __t('Block class suffix'))); ?> - - Html->useTag('fieldsetend'); ?> - Html->useTag('fieldsetend'); ?> - - - Form->input(__t('Save block'), array('type' => 'submit')); ?> -Form->end(); ?> \ No newline at end of file diff --git a/app/Plugin/block/View/Manage/admin_index.ctp b/app/Plugin/block/View/Manage/admin_index.ctp deleted file mode 100644 index 8fbbb40e..00000000 --- a/app/Plugin/block/View/Manage/admin_index.ctp +++ /dev/null @@ -1,239 +0,0 @@ -" . __t('clone') . " | ", - "" . __t('configure') . " | ", - "{php} return (('{Block.module}' == 'block' || {Block.clone_of} != 0) ? \"" . __t('delete') . " | \" : ''); {/php}", - "" . __t('move up') . " | ", - "" . __t('move down') . "" - ); - - $displayFields = array( - 'columns' => array( - __t('Block') => array( - 'value' => "{php} - if ('{Block.title}' == '') { - if ('{Menu.title}' != '') { - return '{Menu.title}'; - } - - return '{Block.module}_{Block.delta}'; - } - return '{Block.title}
      {BlockCustom.description}'; - {/php}", - 'tdOptions' => array('width' => '60%') - ), - __t('Region') => array( - 'value' => null - ), - __t('Actions') => array( - 'value' => implode(' ', $actions), - 'thOptions' => array('align' => 'right'), - 'tdOptions' => array('align' => 'right') - ), - ), - 'paginate' => false, - 'headerPosition' => 'top', - 'tableOptions' => array('width' => '100%') # table attributes - ); -?> - -Html->useTag('fieldsetstart', '' . $themes[Configure::read('Variable.site_theme')]['info']['name'] . ''); ?> - -Html->useTag('fieldsetend'); ?> - -
    -
    - - -Html->useTag('fieldsetstart', '' . $themes[Configure::read('Variable.admin_theme')]['info']['name'] . ''); ?> - -Html->useTag('fieldsetend'); ?> - -
    -
    - - -Html->useTag('fieldsetstart', '' . __t('Unassigned') . ''); ?> - -Html->useTag('fieldsetend'); ?> - - \ No newline at end of file diff --git a/app/Plugin/block/block.yaml b/app/Plugin/block/block.yaml deleted file mode 100644 index 2e5a0a2c..00000000 --- a/app/Plugin/block/block.yaml +++ /dev/null @@ -1,5 +0,0 @@ -name: Block -description: Controls the visual building blocks a page is constructed with. Blocks are boxes of content rendered into an area, or region, of a web page. -category: Core -version: 1.0 -core: 1.x \ No newline at end of file diff --git a/app/Plugin/block/webroot/css/empty b/app/Plugin/block/webroot/css/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/webroot/img/empty b/app/Plugin/block/webroot/img/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/block/webroot/js/empty b/app/Plugin/block/webroot/js/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/comment/Config/bootstrap.php b/app/Plugin/comment/Config/bootstrap.php deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/comment/Config/routes.php b/app/Plugin/comment/Config/routes.php deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/comment/Controller/CommentAppController.php b/app/Plugin/comment/Controller/CommentAppController.php deleted file mode 100644 index fc9d0bac..00000000 --- a/app/Plugin/comment/Controller/CommentAppController.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class CommentAppController extends AppController { - public $uses = array('Comment.Comment'); - - public function countUnpublished() { - $count = $this->Comment->find('count', - array( - 'conditions' => array( - 'Comment.status' => 0 - ) - ) - ); - - $this->set('countUnpublished', $count); - - return $count; - } -} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/CommentController.php b/app/Plugin/comment/Controller/CommentController.php deleted file mode 100644 index 68cf61f3..00000000 --- a/app/Plugin/comment/Controller/CommentController.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class CommentController extends CommentAppController { - public $name = 'Comment'; - public $uses = array(); - - public function admin_index() { - $this->redirect('/admin/comment/published'); - } -} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/Component/empty b/app/Plugin/comment/Controller/Component/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Plugin/comment/Controller/PublishedController.php b/app/Plugin/comment/Controller/PublishedController.php deleted file mode 100644 index de4f3dc0..00000000 --- a/app/Plugin/comment/Controller/PublishedController.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class PublishedController extends CommentAppController { - public $name = 'Published'; - public $uses = array('Comment.Comment'); - - public function admin_index() { - if (isset($this->data['Comment']['update'])) { - if (isset($this->data['Items']['id'])) { - $update = ( !in_array($this->data['Comment']['update'], array('delete'))); - - switch ($this->data['Comment']['update']) { - case 'approve': - default: - $data = array( 'field' => 'status', 'value' => 1); - break; - - case 'unapprove': - $data = array('field' => 'status', 'value' => 0); - break; - } - - foreach ($this->data['Items']['id'] as $key => $id) { - if ($update) { # update node - $this->Comment->id = $id; - $this->Comment->saveField($data['field'], $data['value'], false); - } else { # delete node - switch ($this->data['Comment']['update']) { - case 'delete': - $this->Comment->delete($id); - break; - } - } - } - } - - $this->redirect($this->referer()); - } - - $results = $this->paginate('Comment', array('Comment.status' => 1)); - - $this->countUnpublished(); - $this->set('results', $results); - $this->setCrumb('/admin/node/contents'); - $this->setCrumb( array(__t('Comments'))); - $this->title(__t('Published comments')); - } -} \ No newline at end of file diff --git a/app/Plugin/comment/Controller/UnpublishedController.php b/app/Plugin/comment/Controller/UnpublishedController.php deleted file mode 100644 index 0a27c75e..00000000 --- a/app/Plugin/comment/Controller/UnpublishedController.php +++ /dev/null @@ -1,56 +0,0 @@ - - * @link http://cms.quickapps.es - */ -class UnpublishedController extends CommentAppController { - public $name = 'Unpublished'; - public $uses = array('Comment.Comment'); - - public function admin_index() { - if (isset($this->data['Comment']['update'])) { - if (isset($this->data['Items']['id'])) { - $update = ( !in_array($this->data['Comment']['update'], array('delete'))); - - switch ($this->data['Comment']['update']) { - case 'approve': - default: - $data = array( 'field' => 'status', 'value' => 1); - break; - case 'unapprove': - $data = array('field' => 'status', 'value' => 0); - break; - } - - foreach ($this->data['Items']['id'] as $key => $id) { - if ($update) { # update node - $this->Comment->id = $id; - $this->Comment->saveField($data['field'], $data['value'], false); - } else { # delete node - switch ($this->data['Comment']['update']) { - case 'delete': - $this->Comment->delete($id); - break; - } - } - } - } - - $this->redirect($this->referer()); - } - - $results = $this->paginate('Comment', array('Comment.status' => 0)); - - $this->countUnpublished(); - $this->set('results', $results); - $this->setCrumb('/admin/node/contents'); - $this->setCrumb(array(__t('Comments'))); - $this->title(__t('Unpublished comments')); - } -} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/Nbbc.php b/app/Plugin/comment/Lib/Nbbc.php deleted file mode 100644 index 34ad214d..00000000 --- a/app/Plugin/comment/Lib/Nbbc.php +++ /dev/null @@ -1,2049 +0,0 @@ - '\[', '<' => '<', '{' => '\{', '(' => '\(' ); -$regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); -$endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); -if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; -$e = $regex_endmarkers[$tagmarker]; -$b = $regex_beginmarkers[$tagmarker]; -$this->tagmarker = $tagmarker; -$this->end_tagmarker = $endmarkers[$tagmarker]; -$this->pat_main = "/( " -. "{$b}" -. "(?! -- | ' | !-- | {$b}{$b} )" -. "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" -. "{$e}" -. "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" -. "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" -. "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" -. "| -----+" -. "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" -. "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" -. "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" -. " )/Dx"; -$this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); -$this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; -$this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; -$this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; -$this->ptr = 0; -$this->unget = false; -$this->state = BBCODE_LEXSTATE_TEXT; -$this->verbatim = false; -$this->token = BBCODE_EOI; -$this->tag = false; -$this->text = ""; -} -function GuessTextLength() { -$length = 0; -$ptr = 0; -$state = BBCODE_LEXSTATE_TEXT; -while ($ptr < count($this->input)) { -$text = $this->input[$ptr++]; -if ($state == BBCODE_LEXSTATE_TEXT) { -$state = BBCODE_LEXSTATE_TAG; -$length += strlen($text); -} -else { -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$state = BBCODE_LEXSTATE_TEXT; -$length++; -break; -default: -$state = BBCODE_LEXSTATE_TEXT; -$length += strlen($text); -break; -case 40: -case 60: -case 91: -case 123: -$state = BBCODE_LEXSTATE_TEXT; -break; -} -} -} -return $length; -} -function NextToken() { -if ($this->unget) { -$this->unget = false; -return $this->token; -} -while (true) { -if ($this->ptr >= count($this->input)) { -$this->text = ""; -$this->tag = false; -return $this->token = BBCODE_EOI; -} -$this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", -$this->input[$this->ptr++]); -if ($this->verbatim) { -$this->tag = false; -if ($this->state == BBCODE_LEXSTATE_TEXT) { -$this->state = BBCODE_LEXSTATE_TAG; -$token_type = BBCODE_TEXT; -} -else { -$this->state = BBCODE_LEXSTATE_TEXT; -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$token_type = BBCODE_NL; -break; -default: -$token_type = BBCODE_WS; -break; -case 45: -case 40: -case 60: -case 91: -case 123: -$token_type = BBCODE_TEXT; -break; -} -} -if (strlen($this->text) > 0) -return $this->token = $token_type; -} -else if ($this->state == BBCODE_LEXSTATE_TEXT) { -$this->state = BBCODE_LEXSTATE_TAG; -$this->tag = false; -if (strlen($this->text) > 0) -return $this->token = BBCODE_TEXT; -} -else { -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_NL; -case 45: -if (preg_match("/^-----/", $this->text)) { -$this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_TAG; -} -else { -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -if (strlen($this->text) > 0) -return $this->token = BBCODE_TEXT; -continue; -} -default: -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_WS; -case 40: -case 60: -case 91: -case 123: -if (preg_match($this->pat_comment, $this->text)) { -$this->state = BBCODE_LEXSTATE_TEXT; -continue; -} -if (preg_match($this->pat_comment2, $this->text)) { -$this->state = BBCODE_LEXSTATE_TEXT; -continue; -} -if (preg_match($this->pat_wiki, $this->text, $matches)) { -$this->tag = Array('_name' => 'wiki', '_endtag' => false, -'_default' => @$matches[1], 'title' => @$matches[2]); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_TAG; -} -$this->tag = $this->Internal_DecodeTag($this->text); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); -} -} -} -} -function UngetToken() { -if ($this->token !== BBCODE_EOI) -$this->unget = true; -} -function PeekToken() { -$result = $this->NextToken(); -if ($this->token !== BBCODE_EOI) -$this->unget = true; -return $result; -} -function SaveState() { -return Array( -'token' => $this->token, -'text' => $this->text, -'tag' => $this->tag, -'state' => $this->state, -'input' => $this->input, -'ptr' => $this->ptr, -'unget' => $this->unget, -'verbatim' => $this->verbatim -); -} -function RestoreState($state) { -if (!is_array($state)) return; -$this->token = @$state['token']; -$this->text = @$state['text']; -$this->tag = @$state['tag']; -$this->state = @$state['state']; -$this->input = @$state['input']; -$this->ptr = @$state['ptr']; -$this->unget = @$state['unget']; -$this->verbatim = @$state['verbatim']; -} -function Internal_StripQuotes($string) { -if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) -return $matches[1]; -else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) -return $matches[1]; -else return $string; -} -function Internal_ClassifyPiece($ptr, $pieces) { -if ($ptr >= count($pieces)) return -1; -$piece = $pieces[$ptr]; -if ($piece == '=') return '='; -else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; -else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; -else return 'A'; -} -function Internal_DecodeTag($tag) { -$result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', -'_hasend' => false, '_end' => false, '_default' => false); -$tag = substr($tag, 1, strlen($tag)-2); -$ch = ord(substr($tag, 0, 1)); -if ($ch >= 0 && $ch <= 32) return $result; -$pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", -$tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); -$ptr = 0; -if (count($pieces) < 1) return $result; -if (@substr($pieces[$ptr], 0, 1) == '/') { -$result['_name'] = strtolower(substr($pieces[$ptr++], 1)); -$result['_end'] = true; -} -else { -$result['_name'] = strtolower($pieces[$ptr++]); -$result['_end'] = false; -} -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -$params = Array(); -if ($type != '=') { -$result['_default'] = false; -$params[] = Array('key' => '', 'value' => ''); -} -else { -$ptr++; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type == "\"") -$value = $this->Internal_StripQuotes($pieces[$ptr++]); -else { -$after_space = false; -$start = $ptr; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { -if ($type == ' ') $after_space = true; -if ($type == '=' && $after_space) break; -$ptr++; -} -if ($type == -1) $ptr--; -if ($type == '=') { -$ptr--; -while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') -$ptr--; -while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') -$ptr--; -} -$value = ""; -for (; $start <= $ptr; $start++) { -if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') -$value .= " "; -else $value .= $this->Internal_StripQuotes($pieces[$start]); -} -$value = trim($value); -$ptr++; -} -$result['_default'] = $value; -$params[] = Array('key' => '', 'value' => $value); -} -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { -while ($type == ' ') { -$ptr++; -$type = $this->Internal_ClassifyPiece($ptr, $pieces); -} -if ($type == 'A' || $type == '"') -$key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); -else if ($type == '=') { -$ptr++; -continue; -} -else if ($type == -1) break; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type != '=') -$value = $this->Internal_StripQuotes($key); -else { -$ptr++; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type == '"') { -$value = $this->Internal_StripQuotes($pieces[$ptr++]); -} -else if ($type != -1) { -$value = $pieces[$ptr++]; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 -&& $type != ' ') -$value .= $pieces[$ptr++]; -} -else $value = ""; -} -if (substr($key, 0, 1) != '_') -$result[$key] = $value; -$params[] = Array('key' => $key, 'value' => $value); -} -$result['_params'] = $params; -return $result; -} -} - -class BBCodeLibrary { -var $default_smileys = Array( -':)' => 'smile.gif', ':-)' => 'smile.gif', -'=)' => 'smile.gif', '=-)' => 'smile.gif', -':(' => 'frown.gif', ':-(' => 'frown.gif', -'=(' => 'frown.gif', '=-(' => 'frown.gif', -':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', -'=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', -'>:('=> 'angry.gif', '>:-('=> 'angry.gif', -'>=('=> 'angry.gif', '>=-('=> 'angry.gif', -'D:' => 'angry.gif', 'D-:' => 'angry.gif', -'D=' => 'angry.gif', 'D-=' => 'angry.gif', -'>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', -'>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', -'>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', -'>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', -'>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', -'>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', -'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', -'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', -':O' => 'surprise.gif', ':-O' => 'surprise.gif', -'=O' => 'surprise.gif', '=-O' => 'surprise.gif', -':?' => 'confuse.gif', ':-?' => 'confuse.gif', -'=?' => 'confuse.gif', '=-?' => 'confuse.gif', -':s' => 'worry.gif', ':-S' => 'worry.gif', -'=s' => 'worry.gif', '=-S' => 'worry.gif', -':|' => 'neutral.gif', ':-|' => 'neutral.gif', -'=|' => 'neutral.gif', '=-|' => 'neutral.gif', -':I' => 'neutral.gif', ':-I' => 'neutral.gif', -'=I' => 'neutral.gif', '=-I' => 'neutral.gif', -':/' => 'irritated.gif', ':-/' => 'irritated.gif', -'=/' => 'irritated.gif', '=-/' => 'irritated.gif', -':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', -'=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', -':P' => 'tongue.gif', ':-P' => 'tongue.gif', -'=P' => 'tongue.gif', '=-P' => 'tongue.gif', -'X-P' => 'tongue.gif', -'8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', -'B)' => 'cool.gif', 'B-)' => 'cool.gif', -';)' => 'wink.gif', ';-)' => 'wink.gif', -';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', -'^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', -'>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', -'<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', -'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', -';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', -':3' => 'smile3.gif', ':-3' => 'smile3.gif', -'=3' => 'smile3.gif', '=-3' => 'smile3.gif', -';3' => 'wink3.gif', ';-3' => 'wink3.gif', -'' => 'teeth.gif', '' => 'teeth.gif', -'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', -':blue:' => 'blue.gif', -':zzz:' => 'sleepy.gif', -'<3' => 'heart.gif', -':star:' => 'star.gif', -); -var $default_tag_rules = Array( -'b' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'i' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'u' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'s' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'font' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), -'method' => 'DoFont', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'color' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), -'template' => '{$_content/v}', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'size' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'allow' => Array('_default' => '/^[0-9.]+$/D'), -'method' => 'DoSize', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'sup' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'sub' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'spoiler' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'acronym' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'template' => '{$_content/v}', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'url' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoURL', -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'content' => BBCODE_REQUIRED, -'plain_start' => "", -'plain_end' => "", -'plain_content' => Array('_content', '_default'), -'plain_link' => Array('_default', '_content'), -), -'email' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoEmail', -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'content' => BBCODE_REQUIRED, -'plain_start' => "", -'plain_end' => "", -'plain_content' => Array('_content', '_default'), -'plain_link' => Array('_default', '_content'), -), -'wiki' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoWiki", -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'plain_start' => "[", -'plain_end' => "]", -'plain_content' => Array('title', '_default'), -'plain_link' => Array('_default', '_content'), -), -'img' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoImage", -'class' => 'image', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'end_tag' => BBCODE_REQUIRED, -'content' => BBCODE_REQUIRED, -'plain_start' => "[image]", -'plain_content' => Array(), -), -'rule' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoRule", -'class' => 'block', -'allow_in' => Array('listitem', 'block', 'columns'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "sns", -'after_tag' => "sns", -'plain_start' => "\n-----\n", -'plain_end' => "", -'plain_content' => Array(), -), -'br' => Array( -'mode' => BBCODE_MODE_SIMPLE, -'simple_start' => "
    \n", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "s", -'after_tag' => "s", -'plain_start' => "\n", -'plain_end' => "", -'plain_content' => Array(), -), -'left' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'right' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'center' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'indent' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'columns' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'class' => 'columns', -'allow_in' => Array('listitem', 'block', 'columns'), -'end_tag' => BBCODE_REQUIRED, -'content' => BBCODE_REQUIRED, -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'nextcol' => Array( -'simple_start' => "\n\n", -'class' => 'nextcol', -'allow_in' => Array('columns'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "", -), -'code' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'template' => "\n
    \n
    Code:
    \n
    {\$_content/v}
    \n
    \n", -'class' => 'code', -'allow_in' => Array('listitem', 'block', 'columns'), -'content' => BBCODE_VERBATIM, -'before_tag' => "sns", -'after_tag' => "sn", -'before_endtag' => "sn", -'after_endtag' => "sns", -'plain_start' => "\nCode:\n", -'plain_end' => "\n", -), -'quote' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoQuote", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\nQuote:\n", -'plain_end' => "\n", -), -'list' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoList', -'class' => 'list', -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'*' => Array( -'simple_start' => "
  • ", -'simple_end' => "
  • \n", -'class' => 'listitem', -'allow_in' => Array('list'), -'end_tag' => BBCODE_OPTIONAL, -'before_tag' => "s", -'after_tag' => "s", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n * ", -'plain_end' => "\n", -), -); -function DoURL($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); -if ($bbcode->IsValidURL($url)) { -if ($bbcode->debug) -print "ISVALIDURL
    "; -if ($bbcode->url_targetable !== false && isset($params['target'])) -$target = " target=\"" . htmlspecialchars($params['target']) . "\""; -else $target = ""; -if ($bbcode->url_target !== false) -if (!($bbcode->url_targetable == 'override' && isset($params['target']))) -$target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; -return '' . $content . ''; -} -else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); -} -function DoEmail($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); -if ($bbcode->IsValidEmail($email)) -return '' . $content . ''; -else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); -} -function DoSize($bbcode, $action, $name, $default, $params, $content) { -switch ($default) { -case '0': $size = '.5em'; break; -case '1': $size = '.67em'; break; -case '2': $size = '.83em'; break; -default: -case '3': $size = '1.0em'; break; -case '4': $size = '1.17em'; break; -case '5': $size = '1.5em'; break; -case '6': $size = '2.0em'; break; -case '7': $size = '2.5em'; break; -} -return "$content"; -} -function DoFont($bbcode, $action, $name, $default, $params, $content) { -$fonts = explode(",", $default); -$result = ""; -$special_fonts = Array( -'serif' => 'serif', -'sans-serif' => 'sans-serif', -'sans serif' => 'sans-serif', -'sansserif' => 'sans-serif', -'sans' => 'sans-serif', -'cursive' => 'cursive', -'fantasy' => 'fantasy', -'monospace' => 'monospace', -'mono' => 'monospace', -); -foreach ($fonts as $font) { -$font = trim($font); -if (isset($special_fonts[$font])) { -if (strlen($result) > 0) $result .= ","; -$result .= $special_fonts[$font]; -} -else if (strlen($font) > 0) { -if (strlen($result) > 0) $result .= ","; -$result .= "'$font'"; -} -} -return "$content"; -} -function DoWiki($bbcode, $action, $name, $default, $params, $content) { -$name = $bbcode->Wikify($default); -if ($action == BBCODE_CHECK) -return strlen($name) > 0; -$title = trim(@$params['title']); -if (strlen($title) <= 0) $title = trim($default); -return "wiki_url}$name\" class=\"bbcode_wiki\">" -. htmlspecialchars($title) . ""; -} -function DoImage($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$content = trim($bbcode->UnHTMLEncode(strip_tags($content))); -if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { -if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { -if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { -$info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); -if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { -return "local_img_url}/{$content}") . "\" alt=\"" -. htmlspecialchars(basename($content)) . "\" width=\"" -. htmlspecialchars($info[0]) . "\" height=\"" -. htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; -} -} -} -else if ($bbcode->IsValidURL($content, false)) { -return "\"""; -} -} -return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); -} -function DoRule($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -else return $bbcode->rule_html; -} -function DoQuote($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -if (isset($params['name'])) { -$title = htmlspecialchars(trim($params['name'])) . " wrote"; -if (isset($params['date'])) -$title .= " on " . htmlspecialchars(trim($params['date'])); -$title .= ":"; -if (isset($params['url'])) { -$url = trim($params['url']); -if ($bbcode->IsValidURL($url)) -$title = "" . $title . ""; -} -} -else if (!is_string($default)) -$title = "Quote:"; -else $title = htmlspecialchars(trim($default)) . " wrote:"; -return "\n
    \n
    " -. $title . "
    \n
    " -. $content . "
    \n
    \n"; -} -function DoList($bbcode, $action, $name, $default, $params, $content) { -$list_styles = Array( -'1' => 'decimal', -'01' => 'decimal-leading-zero', -'i' => 'lower-roman', -'I' => 'upper-roman', -'a' => 'lower-alpha', -'A' => 'upper-alpha', -); -$ci_list_styles = Array( -'circle' => 'circle', -'disc' => 'disc', -'square' => 'square', -'greek' => 'lower-greek', -'armenian' => 'armenian', -'georgian' => 'georgian', -); -$ul_types = Array( -'circle' => 'circle', -'disc' => 'disc', -'square' => 'square', -); -$default = trim($default); -if ($action == BBCODE_CHECK) { -if (!is_string($default) || strlen($default) == "") return true; -else if (isset($list_styles[$default])) return true; -else if (isset($ci_list_styles[strtolower($default)])) return true; -else return false; -} -if (!is_string($default) || strlen($default) == "") { -$elem = 'ul'; -$type = ''; -} -else if ($default == '1') { -$elem = 'ol'; -$type = ''; -} -else if (isset($list_styles[$default])) { -$elem = 'ol'; -$type = $list_styles[$default]; -} -else { -$default = strtolower($default); -if (isset($ul_types[$default])) { -$elem = 'ul'; -$type = $ul_types[$default]; -} -else if (isset($ci_list_styles[$default])) { -$elem = 'ol'; -$type = $ci_list_styles[$default]; -} -} -if (strlen($type)) -return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content\n"; -else return "\n<$elem class=\"bbcode_list\">\n$content\n"; -} -} - -class BBCodeEmailAddressValidator { -function check_email_address($strEmailAddress) { -if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { -return false; -} -$intAtSymbol = strrpos($strEmailAddress, '@'); -if ($intAtSymbol === false) { -return false; -} -$arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); -$arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); -$arrTempAddress[0] = preg_replace('/"[^"]+"/' -,'' -,$arrEmailAddress[0]); -$arrTempAddress[1] = $arrEmailAddress[1]; -$strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; -if (strrpos($strTempAddress, '@') !== false) { -return false; -} -if (!$this->check_local_portion($arrEmailAddress[0])) { -return false; -} -if (!$this->check_domain_portion($arrEmailAddress[1])) { -return false; -} -return true; -} -function check_local_portion($strLocalPortion) { -if (!$this->check_text_length($strLocalPortion, 1, 64)) { -return false; -} -$arrLocalPortion = explode('.', $strLocalPortion); -for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { -if (!preg_match('.^(' -. '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' -. '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' -.'|' -. '("[^\\\"]{0,62}")' -.')$.' -,$arrLocalPortion[$i])) { -return false; -} -} -return true; -} -function check_domain_portion($strDomainPortion) { -if (!$this->check_text_length($strDomainPortion, 1, 255)) { -return false; -} -if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' -.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' -,$strDomainPortion) || -preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' -.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' -,$strDomainPortion)) { -return true; -} else { -$arrDomainPortion = explode('.', $strDomainPortion); -if (sizeof($arrDomainPortion) < 2) { -return false; -} -for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { -if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { -return false; -} -if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' -.'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { -return false; -} -} -} -return true; -} -function check_text_length($strText, $intMinimum, $intMaximum) { -$intTextLength = strlen($strText); -if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { -return false; -} else { -return true; -} -} -} - -class BBCode { -var $tag_rules; -var $defaults; -var $current_class; -var $root_class; -var $lost_start_tags; -var $start_tags; -var $allow_ampersand; -var $tag_marker; -var $ignore_newlines; -var $plain_mode; -var $detect_urls; -var $url_pattern; -var $output_limit; -var $text_length; -var $was_limited; -var $limit_tail; -var $limit_precision; -var $smiley_dir; -var $smiley_url; -var $smileys; -var $smiley_regex; -var $enable_smileys; -var $wiki_url; -var $local_img_dir; -var $local_img_url; -var $url_targetable; -var $url_target; -var $rule_html; -var $pre_trim; -var $post_trim; -var $debug; -function BBCode() { -$this->defaults = new BBCodeLibrary; -$this->tag_rules = $this->defaults->default_tag_rules; -$this->smileys = $this->defaults->default_smileys; -$this->enable_smileys = true; -$this->smiley_regex = false; -$this->smiley_dir = $this->GetDefaultSmileyDir(); -$this->smiley_url = $this->GetDefaultSmileyURL(); -$this->wiki_url = $this->GetDefaultWikiURL(); -$this->local_img_dir = $this->GetDefaultLocalImgDir(); -$this->local_img_url = $this->GetDefaultLocalImgURL(); -$this->rule_html = $this->GetDefaultRuleHTML(); -$this->pre_trim = ""; -$this->post_trim = ""; -$this->root_class = 'block'; -$this->lost_start_tags = Array(); -$this->start_tags = Array(); -$this->tag_marker = '['; -$this->allow_ampsersand = false; -$this->current_class = $this->root_class; -$this->debug = false; -$this->ignore_newlines = false; -$this->output_limit = 0; -$this->plain_mode = false; -$this->was_limited = false; -$this->limit_tail = "..."; -$this->limit_precision = 0.15; -$this->detect_urls = false; -$this->url_pattern = '{$text/h}'; -$this->url_targetable = false; -$this->url_target = false; -} -function SetPreTrim($trim = "a") { $this->pre_trim = $trim; } -function GetPreTrim() { return $this->pre_trim; } -function SetPostTrim($trim = "a") { $this->post_trim = $trim; } -function GetPostTrim() { return $this->post_trim; } -function SetRoot($class = 'block') { $this->root_class = $class; } -function SetRootInline() { $this->root_class = 'inline'; } -function SetRootBlock() { $this->root_class = 'block'; } -function GetRoot() { return $this->root_class; } -function SetDebug($enable = true) { $this->debug = $enable; } -function GetDebug() { return $this->debug; } -function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; } -function GetAllowAmpersand() { return $this->allow_ampersand; } -function SetTagMarker($marker = '[') { $this->tag_marker = $marker; } -function GetTagMarker() { return $this->tag_marker; } -function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; } -function GetIgnoreNewlines() { return $this->ignore_newlines; } -function SetLimit($limit = 0) { $this->output_limit = $limit; } -function GetLimit() { return $this->output_limit; } -function SetLimitTail($tail = "...") { $this->limit_tail = $tail; } -function GetLimitTail() { return $this->limit_tail; } -function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; } -function GetLimitPrecision() { return $this->limit_precision; } -function WasLimited() { return $this->was_limited; } -function SetPlainMode($enable = true) { $this->plain_mode = $enable; } -function GetPlainMode() { return $this->plain_mode; } -function SetDetectURLs($enable = true) { $this->detect_urls = $enable; } -function GetDetectURLs() { return $this->detect_urls; } -function SetURLPattern($pattern) { $this->url_pattern = $pattern; } -function GetURLPattern() { return $this->url_pattern; } -function SetURLTargetable($enable) { $this->url_targetable = $enable; } -function GetURLTargetable() { return $this->url_targetable; } -function SetURLTarget($target) { $this->url_target = $target; } -function GetURLTarget() { return $this->url_target; } -function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; } -function RemoveRule($name) { unset($this->tag_rules[$name]); } -function GetRule($name) { return isset($this->tag_rules[$name]) -? $this->tag_rules[$name] : false; } -function ClearRules() { $this->tag_rules = Array(); } -function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name]) -? $this->defaults->default_tag_rules[$name] : false; } -function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name])) -$this->AddRule($name, $this->defaults->default_tag_rules[$name]); -else $this->RemoveRule($name); } -function GetDefaultRules() { return $this->defaults->default_tag_rules; } -function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; } -function SetWikiURL($url) { $this->wiki_url = $url; } -function GetWikiURL($url) { return $this->wiki_url; } -function GetDefaultWikiURL() { return '/?page='; } -function SetLocalImgDir($path) { $this->local_img_dir = $path; } -function GetLocalImgDir() { return $this->local_img_dir; } -function GetDefaultLocalImgDir() { return "img"; } -function SetLocalImgURL($path) { $this->local_img_url = $path; } -function GetLocalImgURL() { return $this->local_img_url; } -function GetDefaultLocalImgURL() { return "img"; } -function SetRuleHTML($html) { $this->rule_html = $html; } -function GetRuleHTML() { return $this->rule_html; } -function GetDefaultRuleHTML() { return "\n
    \n"; } -function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; } -function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; } -function GetSmiley($code) { return isset($this->smileys[$code]) -? $this->smileys[$code] : false; } -function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; } -function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code]) -? $this->defaults->default_smileys[$code] : false; } -function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code]; -$this->smiley_regex = false; } -function GetDefaultSmileys() { return $this->defaults->default_smileys; } -function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys; -$this->smiley_regex = false; } -function SetSmileyDir($path) { $this->smiley_dir = $path; } -function GetSmileyDir() { return $this->smiley_dir; } -function GetDefaultSmileyDir() { return "smileys"; } -function SetSmileyURL($path) { $this->smiley_url = $path; } -function GetSmileyURL() { return $this->smiley_url; } -function GetDefaultSmileyURL() { return "smileys"; } -function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; } -function GetEnableSmileys() { return $this->enable_smileys; } -function nl2br($string) { -return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "
    \n", $string); -} -function UnHTMLEncode($string) { -if (function_exists("html_entity_decode")) -return html_entity_decode($string); -$string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); -$string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string); -$trans_tbl = get_html_translation_table(HTML_ENTITIES); -$trans_tbl = array_flip($trans_tbl); -return strtr($string, $trans_tbl); -} -function Wikify($string) { -return rawurlencode(str_replace(" ", "_", -trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string)))); -} -function IsValidURL($string, $email_too = true) { -if (preg_match("/^ -(?:https?|ftp):\\/\\/ -(?: -(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+ -[a-zA-Z0-9] -(?:[a-zA-Z0-9-]*[a-zA-Z0-9])? -| -\\[ -(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} -(?: -25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]: -(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ -) -\\] -) -(?::[0-9]{1,5})? -(?:[\\/\\?\\#][^\\n\\r]*)? -$/Dx", $string)) return true; -if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) -return true; -if ($email_too) -if (substr($string, 0, 7) == "mailto:") -return $this->IsValidEmail(substr($string, 7)); -return false; -} -function IsValidEmail($string) { -$validator = new BBCodeEmailAddressValidator; -return $validator->check_email_address($string); -/* -return preg_match("/^ -(?: -[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+ -(?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)* -| -\"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\" -) -@ -(?: -(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ -[a-z0-9] -(?:[a-z0-9-]*[a-z0-9])? -| -\\[ -(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} -(?: -25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: -(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ -) -\\] -) -$/Dx", $string); -*/ -} -function HTMLEncode($string) { -if (!$this->allow_ampersand) -return htmlspecialchars($string); -else return str_replace(Array('<', '>', '"'), -Array('<', '>', '"'), $string); -} -function FixupOutput($string) { -if (!$this->detect_urls) { -$output = $this->Internal_ProcessSmileys($string); -} -else { -$chunks = $this->Internal_AutoDetectURLs($string); -$output = Array(); -if (count($chunks)) { -$is_a_url = false; -foreach ($chunks as $index => $chunk) { -if (!$is_a_url) { -$chunk = $this->Internal_ProcessSmileys($chunk); -} -$output[] = $chunk; -$is_a_url = !$is_a_url; -} -} -$output = implode("", $output); -} -return $output; -} -function Internal_ProcessSmileys($string) { -if (!$this->enable_smileys || $this->plain_mode) { -$output = $this->HTMLEncode($string); -} -else { -if ($this->smiley_regex === false) { -$this->Internal_RebuildSmileys(); -} -$tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE); -if (count($tokens) <= 1) { -$output = $this->HTMLEncode($string); -} -else { -$output = ""; -$is_a_smiley = false; -foreach ($tokens as $token) { -if (!$is_a_smiley) { -$output .= $this->HTMLEncode($token); -} -else { -if (isset($this->smiley_info[$token])) { -$info = $this->smiley_info[$token]; -} -else { -$info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]); -$this->smiley_info[$token] = $info; -} -$alt = htmlspecialchars($token); -$output .= "smiley_url . '/' . $this->smileys[$token]) -. "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" -. " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />"; -} -$is_a_smiley = !$is_a_smiley; -} -} -} -return $output; -} -function Internal_RebuildSmileys() { -$regex = Array("/(?smileys as $code => $filename) { -if (!$first) $regex[] = "|"; -$regex[] = preg_quote("$code", '/'); -$first = false; -} -$regex[] = ")(?![\\w])/"; -$this->smiley_regex = implode("", $regex); -} -function Internal_AutoDetectURLs($string) { -$output = preg_split("/( (?: -(?:https?|ftp) : \\/* -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} ) -) -(?: : [0-9]+ )? -(?! [a-zA-Z0-9.:-] ) -(?: -\\/ -[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* -)? -(?: -[?#] -[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ -)? -) | (?: -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -) -(?: : [0-9]+ )? -(?! [a-zA-Z0-9.:-] ) -(?: -\\/ -[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* -)? -(?: -[?#] -[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ -)? -) | (?: -[a-zA-Z0-9._-]{2,} @ -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -) -))/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE); -if (count($output) > 1) { -$is_a_url = false; -foreach ($output as $index => $token) { -if ($is_a_url) { -if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) { -$url = "mailto:" . $token; -} -else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) { -$url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3]; -} -else { -preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches); -$url = "http:/" . "/" . $matches[1] . "/" . $matches[2]; -} -$params = @parse_url($url); -if (!is_array($params)) $params = Array(); -$params['url'] = $url; -$params['link'] = $url; -$params['text'] = $token; -$output[$index] = $this->FillTemplate($this->url_pattern, $params); -} -$is_a_url = !$is_a_url; -} -} -return $output; -} -function FillTemplate($template, $insert_array, $default_array = Array()) { -$pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template, --1, PREG_SPLIT_DELIM_CAPTURE); -if (count($pieces) <= 1) -return $template; -$result = Array(); -$is_an_insert = false; -foreach ($pieces as $piece) { -if (!$is_an_insert) { -$result[] = $piece; -} -else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) { -$result[] = $piece; -} -else { -if (isset($insert_array[$matches[1]])) -$value = @$insert_array[$matches[1]]; -else $value = @$default_array[$matches[1]]; -if (strlen(@$matches[2])) { -foreach (split(".", substr($matches[2], 1)) as $index) { -if (is_array($value)) -$value = @$value[$index]; -else if (is_object($value)) { -$value = (array)$value; -$value = @$value[$index]; -} -else $value = ""; -} -} -switch (gettype($value)) { -case 'boolean': $value = $value ? "true" : "false"; break; -case 'integer': $value = (string)$value; break; -case 'double': $value = (string)$value; break; -case 'string': break; -default: $value = ""; break; -} -if (strlen(@$matches[3])) -$flags = array_flip(str_split($matches[3])); -else $flags = Array(); -if (!isset($flags['v'])) { -if (isset($flags['w'])) -$value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value); -if (isset($flags['t'])) $value = trim($value); -if (isset($flags['b'])) $value = basename($value); -if (isset($flags['e'])) $value = $this->HTMLEncode($value); -else if (isset($flags['k'])) $value = $this->Wikify($value); -else if (isset($flags['h'])) $value = htmlspecialchars($value); -else if (isset($flags['u'])) $value = urlencode($value); -if (isset($flags['n'])) $value = $this->nl2br($value); -} -$result[] = $value; -} -$is_an_insert = !$is_an_insert; -} -return implode("", $result); -} -function Internal_CollectText($array, $start = 0) { -ob_start(); -for ($start = intval($start), $end = count($array); $start < $end; $start++) -print $array[$start][BBCODE_STACK_TEXT]; -$output = ob_get_contents(); -ob_end_clean(); -return $output; -} -function Internal_CollectTextReverse($array, $start = 0, $end = 0) { -ob_start(); -for ($start = intval($start); $start >= $end; $start--) -print $array[$start][BBCODE_STACK_TEXT]; -$output = ob_get_contents(); -ob_end_clean(); -return $output; -} -function Internal_GenerateOutput($pos) { -$output = Array(); -while (count($this->stack) > $pos) { -$token = array_pop($this->stack); -if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) { -$output[] = $token; -} -else { -$name = @$token[BBCODE_STACK_TAG]['_name']; -$rule = @$this->tag_rules[$name]; -$end_tag = @$rule['end_tag']; -if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED; -else $end_tag = $rule['end_tag']; -array_pop($this->start_tags[$name]); -if ($end_tag == BBCODE_PROHIBIT) { -$output[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TAG => false, -BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT], -BBCODE_STACK_CLASS => $this->current_class, -); -} -else { -if ($end_tag == BBCODE_REQUIRED) -@$this->lost_start_tags[$name] += 1; -$end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output); -$this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output); -$tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end); -$this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack); -$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]); -$tag_output = $this->DoTag(BBCODE_OUTPUT, $name, -@$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body); -$output = Array(Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TAG => false, -BBCODE_STACK_TEXT => $tag_output, -BBCODE_STACK_CLASS => $this->current_class -)); -} -} -} -$this->Internal_ComputeCurrentClass(); -return $output; -} -function Internal_RewindToClass($class_list) { -$pos = count($this->stack) - 1; -while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) -$pos--; -if ($pos < 0) { -if (!in_array($this->root_class, $class_list)) -return false; -} -$output = $this->Internal_GenerateOutput($pos+1); -while (count($output)) { -$token = array_pop($output); -$token[BBCODE_STACK_CLASS] = $this->current_class; -$this->stack[] = $token; -} -return true; -} -function Internal_FinishTag($tag_name) { -if (strlen($tag_name) <= 0) -return false; -if (isset($this->start_tags[$tag_name]) -&& count($this->start_tags[$tag_name])) -$pos = array_pop($this->start_tags[$tag_name]); -else $pos = -1; -if ($pos < 0) return false; -$newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'], -$pos+1, $this->stack); -$delta = $newpos - ($pos+1); -$output = $this->Internal_GenerateOutput($newpos); -$newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'], -0, $output); -$output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend); -while ($delta-- > 0) -array_pop($this->stack); -$this->Internal_ComputeCurrentClass(); -return $output; -} -function Internal_ComputeCurrentClass() { -if (count($this->stack) > 0) -$this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS]; -else $this->current_class = $this->root_class; -} -function Internal_DumpStack($array = false, $raw = false) { -if (!$raw) $string = ""; -else $string = ""; -if ($array === false) -$array = $this->stack; -foreach ($array as $item) { -switch (@$item[BBCODE_STACK_TOKEN]) { -case BBCODE_TEXT: -$string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" "; -break; -case BBCODE_WS: -$string .= "WS "; -break; -case BBCODE_NL: -$string .= "NL "; -break; -case BBCODE_TAG: -$string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] "; -break; -default: -$string .= "unknown "; -break; -} -} -if (!$raw) $string .= ""; -return $string; -} -function Internal_CleanupWSByPoppingStack($pattern, &$array) { -if (strlen($pattern) <= 0) return; -$oldlen = count($array); -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS) -array_pop($array); -break; -case 'n': -if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL) -array_pop($array); -break; -case 'a': -while (count($array) > 0 -&& (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS -|| $token == BBCODE_NL)) -array_pop($array); -break; -} -} -if (count($array) != $oldlen) { -$this->Internal_ComputeCurrentClass(); -} -} -function Internal_CleanupWSByEatingInput($pattern) { -if (strlen($pattern) <= 0) return; -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -$token_type = $this->lexer->NextToken(); -while ($token_type == BBCODE_WS) { -$token_type = $this->lexer->NextToken(); -} -$this->lexer->UngetToken(); -break; -case 'n': -$token_type = $this->lexer->NextToken(); -if ($token_type != BBCODE_NL) -$this->lexer->UngetToken(); -break; -case 'a': -$token_type = $this->lexer->NextToken(); -while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) { -$token_type = $this->lexer->NextToken(); -} -$this->lexer->UngetToken(); -break; -} -} -} -function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) { -if (strlen($pattern) <= 0) return $pos; -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) -$pos++; -break; -case 'n': -if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) -$pos++; -break; -case 'a': -while ($pos < count($array) -&& (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) -$pos++; -break; -} -} -return $pos; -} -function Internal_LimitText($string, $limit) { -$chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE); -$output = ""; -foreach ($chunks as $chunk) { -if (strlen($output) + strlen($chunk) > $limit) -break; -$output .= $chunk; -} -$output = rtrim($output); -return $output; -} -function Internal_DoLimit() { -$this->Internal_CleanupWSByPoppingStack("a", $this->stack); -if (strlen($this->limit_tail) > 0) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->limit_tail, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->was_limited = true; -} -function DoTag($action, $tag_name, $default_value, $params, $contents) { -$tag_rule = @$this->tag_rules[$tag_name]; -switch ($action) { -case BBCODE_CHECK: -if (isset($tag_rule['allow'])) { -foreach ($tag_rule['allow'] as $param => $pattern) { -if ($param == '_content') $value = $contents; -else if ($param == '_defaultcontent') { -if (strlen($default_value)) -$value = $default_value; -else $value = $contents; -} -else { -if (isset($params[$param])) -$value = $params[$param]; -else $value = @$tag_rule['default'][$param]; -} -if (!preg_match($pattern, $value)) { -return false; -} -} -return true; -} -switch (@$tag_rule['mode']) { -default: -case BBCODE_MODE_SIMPLE: -$result = true; -break; -case BBCODE_MODE_ENHANCED: -$result = true; -break; -case BBCODE_MODE_INTERNAL: -$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_LIBRARY: -$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_CALLBACK: -$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -} -return $result; -case BBCODE_OUTPUT: -if ($this->plain_mode) { -if (!isset($tag_rule['plain_content'])) -$plain_content = Array('_content'); -else $plain_content = $tag_rule['plain_content']; -$result = $possible_content = ""; -foreach ($plain_content as $possible_content) { -if ($possible_content == '_content' -&& strlen($contents) > 0) { -$result = $contents; -break; -} -if (isset($params[$possible_content]) -&& strlen($params[$possible_content]) > 0) { -$result = htmlspecialchars($params[$possible_content]); -break; -} -} -$start = @$tag_rule['plain_start']; -$end = @$tag_rule['plain_end']; -if (isset($tag_rule['plain_link'])) { -$link = $possible_content = ""; -foreach ($tag_rule['plain_link'] as $possible_content) { -if ($possible_content == '_content' -&& strlen($contents) > 0) { -$link = $this->UnHTMLEncode(strip_tags($contents)); -break; -} -if (isset($params[$possible_content]) -&& strlen($params[$possible_content]) > 0) { -$link = $params[$possible_content]; -break; -} -} -$params = @parse_url($link); -if (!is_array($params)) $params = Array(); -$params['link'] = $link; -$params['url'] = $link; -$start = $this->FillTemplate($start, $params); -$end = $this->FillTemplate($end, $params); -} -return $start . $result . $end; -} -switch (@$tag_rule['mode']) { -default: -case BBCODE_MODE_SIMPLE: -$result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end']; -break; -case BBCODE_MODE_ENHANCED: -$result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents); -break; -case BBCODE_MODE_INTERNAL: -$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_LIBRARY: -$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_CALLBACK: -$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -} -return $result; -default: -return false; -} -} -function Internal_DoEnhancedTag($tag_rule, $params, $contents) { -$params['_content'] = $contents; -$params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents; -return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']); -} -function Internal_UpdateParamsForMissingEndTag(&$params) { -switch ($this->tag_marker) { -case '[': $tail_marker = ']'; break; -case '<': $tail_marker = '>'; break; -case '{': $tail_marker = '}'; break; -case '(': $tail_marker = ')'; break; -default: $tail_marker = $this->tag_marker; break; -} -$params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker; -} -function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) { -if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, ""); -$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) { -$state = $this->lexer->SaveState(); -$end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker; -$start = count($this->stack); -$this->lexer->verbatim = true; -while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) { -if ($this->lexer->text == $end_tag) { -$end_tag_params = $this->lexer->tag; -break; -} -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$text = $this->Internal_LimitText($this->lexer->text, -$this->output_limit - $this->text_length); -if (strlen($text) > 0) { -$this->text_length += strlen($text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->Internal_DoLimit(); -break; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => $token_type, -BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text), -BBCODE_STACK_TAG => $this->lexer->tag, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->lexer->verbatim = false; -if ($token_type == BBCODE_EOI) { -$this->lexer->RestoreState($state); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack); -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack); -$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']); -$content = $this->Internal_CollectText($this->stack, $newstart); -array_splice($this->stack, $start); -$this->Internal_ComputeCurrentClass(); -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); -$tag_params['_endtag'] = $end_tag_params['_tag']; -$tag_params['_hasend'] = true; -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, -@$tag_params['_default'], $tag_params, $content); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Internal_ParseStartTagToken() { -$tag_params = $this->lexer->tag; -$tag_name = @$tag_params['_name']; -if (!isset($this->tag_rules[$tag_name])) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$tag_rule = $this->tag_rules[$tag_name]; -$allow_in = is_array($tag_rule['allow_in']) -? $tag_rule['allow_in'] : Array($this->root_class); -if (!in_array($this->current_class, $allow_in)) { -if (!$this->Internal_RewindToClass($allow_in)) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -} -$end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED; -if ($end_tag == BBCODE_PROHIBIT) { -$this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule); -return; -} -if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -if (@$tag_rule['content'] == BBCODE_VERBATIM) { -$this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule); -return; -} -if (isset($tag_rule['class'])) -$newclass = $tag_rule['class']; -else $newclass = $this->root_class; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => $this->lexer->token, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => $this->lexer->tag, -BBCODE_STACK_CLASS => ($this->current_class = $newclass), -); -if (!isset($this->start_tags[$tag_name])) -$this->start_tags[$tag_name] = Array(count($this->stack)-1); -else $this->start_tags[$tag_name][] = count($this->stack)-1; -} -function Internal_ParseEndTagToken() { -$tag_params = $this->lexer->tag; -$tag_name = @$tag_params['_name']; -$contents = $this->Internal_FinishTag($tag_name); -if ($contents === false) { -if (@$this->lost_start_tags[$tag_name] > 0) { -$this->lost_start_tags[$tag_name]--; -} -else { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -return; -} -$start_tag_node = array_pop($this->stack); -$start_tag_params = $start_tag_node[BBCODE_STACK_TAG]; -$this->Internal_ComputeCurrentClass(); -$this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack); -$start_tag_params['_endtag'] = $tag_params['_tag']; -$start_tag_params['_hasend'] = true; -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'], -$start_tag_params, $contents); -$this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Parse($string) { -$this->lexer = new BBCodeLexer($string, $this->tag_marker); -$this->lexer->debug = $this->debug; -$old_output_limit = $this->output_limit; -if ($this->output_limit > 0) { -if (strlen($string) < $this->output_limit) { -$this->output_limit = 0; -} -else if ($this->limit_precision > 0) { -$guess_length = $this->lexer->GuessTextLength(); -if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) { -$this->output_limit = 0; -} -else { -} -} -} -$this->stack = Array(); -$this->start_tags = Array(); -$this->lost_start_tags = Array(); -$this->text_length = 0; -$this->was_limited = false; -if (strlen($this->pre_trim) > 0) -$this->Internal_CleanupWSByEatingInput($this->pre_trim); -$newline = $this->plain_mode ? "\n" : "
    \n"; -while (true) { -if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) { -break; -} -switch ($token_type) { -case BBCODE_TEXT: -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$text = $this->Internal_LimitText($this->lexer->text, -$this->output_limit - $this->text_length); -if (strlen($text) > 0) { -$this->text_length += strlen($text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -break; -case BBCODE_WS: -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_WS, -BBCODE_STACK_TEXT => $this->lexer->text, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -break; -case BBCODE_NL: -if ($this->ignore_newlines) { -if ($this->output_limit > 0 -&& $this->text_length + 1 >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += 1; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_WS, -BBCODE_STACK_TEXT => "\n", -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -else { -$this->Internal_CleanupWSByPoppingStack("s", $this->stack); -if ($this->output_limit > 0 -&& $this->text_length + 1 >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += 1; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_NL, -BBCODE_STACK_TEXT => $newline, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -$this->Internal_CleanupWSByEatingInput("s"); -} -break; -case BBCODE_TAG: -$this->Internal_ParseStartTagToken(); -break; -case BBCODE_ENDTAG: -$this->Internal_ParseEndTagToken(); -break; -default: -break; -} -} -if (strlen($this->post_trim) > 0) -$this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack); -$result = $this->Internal_GenerateOutput(0); -$result = $this->Internal_CollectTextReverse($result, count($result) - 1); -$this->output_limit = $old_output_limit; -if ($this->plain_mode) { -$result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result); -$result = preg_replace("/(?:[\\x20]*\\n) {2,}[\\x20]*/", "\n\n", $result); -$result = trim($result); -} -return $result; -} -} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/CHANGELOG b/app/Plugin/comment/Lib/nbbc/CHANGELOG deleted file mode 100644 index 3c25684c..00000000 --- a/app/Plugin/comment/Lib/nbbc/CHANGELOG +++ /dev/null @@ -1,185 +0,0 @@ - -2010-09-17 / v1.4.5 / Phantom Inker (inker2576@yahoo.com) - * SECURITY FIX: Fixed a bug that would allow arbitrary HTML - injection via the standard [acronym] tag. - -2010-08-16 / non-released / Phantom Inker (inker2576@yahoo.com) - * Added another unit test for unclosed lists. - -2010-06-05 / v1.4.4 / Phantom Inker (inker2576@yahoo.com) - * Fixed the long-standing bug in verbatim parsing so that - interior content is now truly verbatim and unprocessed. - This allows source code to be correctly represented inside - a [code] block even if it contains things like ['foo']. - * Fixed typos (missing $this-> and wrong variable names) in - SetDefaultRule() and SetWikiURL() that caused them to - reference things that didn't exist. - * Fixed a typo (missing backslash) in IsValidURL() that caused - it to miss some legal URLs. - * Fixed a bug that was incorrectly generating lists in some cases. - -2009-10-31 / non-released / Phantom Inker (inker2576@yahoo.com) - * Fixed a bug in SetWikiURL that referenced an invalid function - parameter. - * Fixed a bug in the lexer that was causing comments to produce - an invalid following state (and thus causing comments to - effectively turn off tag parsing thereafter!). - * Added tests for the comment bug, and for a reported (but not - yet reproduced) quote bug. - -2009-10-10 / v1.4.3 / Phantom Inker (inker2576@yahoo.com) - * Fixed a bug in plain mode that was causing tags processed in - plain mode to throw errors (it was calling a function that - had since been renamed). - * Added tests for plain mode to ensure this omission won't - happen again. - -2009-06-21 / v1.4.2 / Phantom Inker (inker2576@yahoo.com) - * SECURITY FIX: [img] tag was allowing HTML content to be passed - through. - -2009-04-03 / v1.4.1 / Phantom Inker (inker2576@yahoo.com) - * Fixed documentation for [quote] tag's url= attribute. - -2009-03-20 / v1.4.1 / Phantom Inker (inker2576@yahoo.com) - * Added support for SetURLTarget() and GetURLTarget(), and - extended support for SetURLTargeting(). - * Added four more regression tests for URL targeting. - -2009-02-16 / v1.4.0 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Replaced parsing logic for tags so that an equal-sign before - whitespace can be considered part of a tag's value. This - allows [url=http://foo.com?bar=baz] to be parsed the way the - user expects. Note that this does break some (probably - invalid) tags, but overall, the new parsing algorithm seems - to be a win. - * Added several regression tests for the new tag-parsing logic. - * Added SetURLTargetable() and GetURLTargetable(). However, the - default setting is to have this disabled for security reasons. - * Updated [url] tag to support URL targeting, using the standard - target="" form. - * Fixed Wikify() so that wiki links with ' or , in them will be - processed correctly. - * Added the _tag, _endtag, _hasend, and _params tag parameters. - * Added new appendix H for the tag parameters; added additional - documentation for Set/GetURLTargetable(). - * Added $debug flag to BBCodeLexer, replicated from the same flag in - class BBCode itself. - -2009-01-08 / v1.3.4 / Phantom Inker (inker2576@yahoo.com) - * Replaced IsValidEmail() with e-mail validator from AddedBytes.com, - which does a considerably better job of validating incorrect - e-mails. This fixes a bug were some e-mail addresses in [email] - tags were being rejected even though they were legal. - -2008-12-01 / v1.3.3 / Phantom Inker (inker2576@yahoo.com) - * Added regression tests for [[wiki]] tags due to possible issue - identified in debugging CMXpress v0.9.8. - -2008-11-06 / v1.3.2 / Phantom Inker (inker2576@yahoo.com) - * Added regression tests for parsing equal signs in tags per tracker - ID #2220598. - -2008-08-08 / v1.3.1 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Added profiler class and profiling code. - * Added support for removable sections in compressed version. - * Removed major performance bottleneck in Internal_ProcessSmileys() - that cut overall parsing times to about one third of what they - were before. - * Added PHP 4 compatiblity check for str_split; fixes a significant - bug on PHP 4 systems. - * Simplified test script's time output, since the new profiling code can - track it more accurately when debugging. - -2008-08-07 / v1.3 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Exposed the functionality of FillTemplate() as a public function. - * Changed template-filling to have its default encoding be RAW, not - HTMLEncode(). This will affect any ENHANCED rule used with v1.2 - or earlier; those rules MUST be converted to use the new /e - flag or they will not function properly. - * Changed 'plain_link' processing to use FillTemplate(). - * Added URL/domain-name/email-address auto-detection (turned off by default). - * Added conformance tests for URL/etc. auto-detection. - * Added URL/etc. auto-detection APIs to documentation. - * Added 'k' flag and '.' operator to FillTemplate(). - * Added FillTemplate() API to documentation. - * Added FillTemplate() example. - * Changed example programs to use "nbbc.php", not "src/nbbc_main.php". - * Added RSS example to demonstrate limiting and plain modes. - * Changed full example to offer checkboxes for testing various modes. - * Added an appendix containing sample CSS. - * Added usage sections to the documentation for limited-length mode, - plain-HTML mode, and URL-autodetection mode. - * Added new NBBC logo :-) - Stats: 127 KB source, 260 KB user's manual, 131 conformance tests [ALL PASS]. - -2008-08-04 / v1.2 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Added output-limiting mode. - * Added output-limit tails. - * Added fuzzy output limiting. - * Added plain-text mode. - * Added API documentation for output-limit and plain-text mode. - * Changed examples to use XHTML header declarations - * Added limiting example. - * Added strip_tags() calls to [url], [email], and [img] tags for safety. - * Fixed double-encoding bug in [url], [email], and [img] tags when specifying - the URL as content instead of in the tag itself. - * Fixed double-encoding bug in the [color] and [acronym] tags. - -2008-07-30 / v1.1 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Added more conformance tests and expanded some existing ones. - * Added performance-measuring to test_nbbc.php. - * Optimized lexer pattern to return fewer text tokens whenever possible. - * Optimized stack to use symbolic constants instead of strings. - * Optimized whitespace removal to perform fewer lexer calls. - * Optimized parser core when dealing with text/whitespace/newlines. - * Optimized text-collection to use output buffering instead of string cats. - * Optimized smiley conversions for the most common case. - * Several other small optimizations and cleanups. - * Overall performance improvement of about 30%. - * Fixed incorrect rule declaration logic for callback functions. - * Fixed section-link glitch in user's manual. - * Fixed bug that was prohibiting uncompressed version from working right. - * Removed (currently) unused BBCODE_OUTPUT_TEXT declarations (these probably - need to be added in for real at a later date). - * Added sample CSS appendix to user's manual. - * Added short table-of-contents to user's manual for web site. - * Added five example scripts to the new examples/ directory. - Stats: 105 KB source, 217 KB user's manual, 121 conformance tests [ALL PASS]. - -2008-07-29 / v1.0 RELEASE / Phantom Inker (inker2576@yahoo.com) - * Added newline-ignore mode and conformance tests for it. - * Finished writing first-edition user's manual. - Stats: 102 KB source, 212 KB user's manual, 120 conformance tests [ALL PASS]. - -2008-07-24 / v1.0 RC5 / Phantom Inker (inker2576@yahoo.com) - * Added alternate tag-marker modes and conformance tests for it. - * Added ampersand pass-through mode and conformance tests for it. - * Fixed HTML-encoding error in BBCODE_VERBATIM tags; converted [code] - tag to use direct htmlspecialchars() calls. - -2008-07-23 / v1.0 RC4 / Phantom Inker (inker2576@yahoo.com) - * Updated tests to recognize new CSS classes in standard library's output. - * Fixed class-output bugs in standard library. - -2008-07-21 / v1.0 RC3 / Phantom Inker (inker2576@yahoo.com) - * Converted user's manual to new auto-generating format, and wrote - additional documentation. User's manual is now about 90% complete. - * Reworked directory structure to be cleaner, and added support for - generating compacted "nbbc.php" version. - -2008-07-16 / v1.0 RC2 / Phantom Inker (inker2576@yahoo.com) - * Fix decoding issue to be more permissive, which corrects nesting errors - for broken [column] tags and other class-nesting issues. - * Add support for removing floating ending tags whose start tags had been - consumed during output-generation. - -2008-07-14 / v1.0 RC / Phantom Inker (inker2576@yahoo.com) - * Proposed final release candidate: Implements full BBCode language, plus - smileys and wiki-links, with no apparent failures or caveats. - * Added conformance test script with 49 tests. - -2008-07-01 / alpha / Phantom Inker (inker2576@yahoo.com) - * First usable build; no version number. Performs basic compiling and stack - transforms; currently very limited in what it can process. - diff --git a/app/Plugin/comment/Lib/nbbc/LICENSE b/app/Plugin/comment/Lib/nbbc/LICENSE deleted file mode 100644 index 63a497cc..00000000 --- a/app/Plugin/comment/Lib/nbbc/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ - - Copyright (C) 2008-10, the Phantom Inker. All rights reserved. - Portions copyright (c) 2004-2008 AddedBytes.com - - 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. - - THIS SOFTWARE IS PROVIDED BY THE PHANTOM INKER "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. - diff --git a/app/Plugin/comment/Lib/nbbc/nbbc.php b/app/Plugin/comment/Lib/nbbc/nbbc.php deleted file mode 100644 index 34ad214d..00000000 --- a/app/Plugin/comment/Lib/nbbc/nbbc.php +++ /dev/null @@ -1,2049 +0,0 @@ - '\[', '<' => '<', '{' => '\{', '(' => '\(' ); -$regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); -$endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); -if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; -$e = $regex_endmarkers[$tagmarker]; -$b = $regex_beginmarkers[$tagmarker]; -$this->tagmarker = $tagmarker; -$this->end_tagmarker = $endmarkers[$tagmarker]; -$this->pat_main = "/( " -. "{$b}" -. "(?! -- | ' | !-- | {$b}{$b} )" -. "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" -. "{$e}" -. "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" -. "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" -. "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" -. "| -----+" -. "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" -. "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" -. "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" -. " )/Dx"; -$this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); -$this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; -$this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; -$this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; -$this->ptr = 0; -$this->unget = false; -$this->state = BBCODE_LEXSTATE_TEXT; -$this->verbatim = false; -$this->token = BBCODE_EOI; -$this->tag = false; -$this->text = ""; -} -function GuessTextLength() { -$length = 0; -$ptr = 0; -$state = BBCODE_LEXSTATE_TEXT; -while ($ptr < count($this->input)) { -$text = $this->input[$ptr++]; -if ($state == BBCODE_LEXSTATE_TEXT) { -$state = BBCODE_LEXSTATE_TAG; -$length += strlen($text); -} -else { -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$state = BBCODE_LEXSTATE_TEXT; -$length++; -break; -default: -$state = BBCODE_LEXSTATE_TEXT; -$length += strlen($text); -break; -case 40: -case 60: -case 91: -case 123: -$state = BBCODE_LEXSTATE_TEXT; -break; -} -} -} -return $length; -} -function NextToken() { -if ($this->unget) { -$this->unget = false; -return $this->token; -} -while (true) { -if ($this->ptr >= count($this->input)) { -$this->text = ""; -$this->tag = false; -return $this->token = BBCODE_EOI; -} -$this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", -$this->input[$this->ptr++]); -if ($this->verbatim) { -$this->tag = false; -if ($this->state == BBCODE_LEXSTATE_TEXT) { -$this->state = BBCODE_LEXSTATE_TAG; -$token_type = BBCODE_TEXT; -} -else { -$this->state = BBCODE_LEXSTATE_TEXT; -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$token_type = BBCODE_NL; -break; -default: -$token_type = BBCODE_WS; -break; -case 45: -case 40: -case 60: -case 91: -case 123: -$token_type = BBCODE_TEXT; -break; -} -} -if (strlen($this->text) > 0) -return $this->token = $token_type; -} -else if ($this->state == BBCODE_LEXSTATE_TEXT) { -$this->state = BBCODE_LEXSTATE_TAG; -$this->tag = false; -if (strlen($this->text) > 0) -return $this->token = BBCODE_TEXT; -} -else { -switch (ord(substr($this->text, 0, 1))) { -case 10: -case 13: -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_NL; -case 45: -if (preg_match("/^-----/", $this->text)) { -$this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_TAG; -} -else { -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -if (strlen($this->text) > 0) -return $this->token = BBCODE_TEXT; -continue; -} -default: -$this->tag = false; -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_WS; -case 40: -case 60: -case 91: -case 123: -if (preg_match($this->pat_comment, $this->text)) { -$this->state = BBCODE_LEXSTATE_TEXT; -continue; -} -if (preg_match($this->pat_comment2, $this->text)) { -$this->state = BBCODE_LEXSTATE_TEXT; -continue; -} -if (preg_match($this->pat_wiki, $this->text, $matches)) { -$this->tag = Array('_name' => 'wiki', '_endtag' => false, -'_default' => @$matches[1], 'title' => @$matches[2]); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = BBCODE_TAG; -} -$this->tag = $this->Internal_DecodeTag($this->text); -$this->state = BBCODE_LEXSTATE_TEXT; -return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); -} -} -} -} -function UngetToken() { -if ($this->token !== BBCODE_EOI) -$this->unget = true; -} -function PeekToken() { -$result = $this->NextToken(); -if ($this->token !== BBCODE_EOI) -$this->unget = true; -return $result; -} -function SaveState() { -return Array( -'token' => $this->token, -'text' => $this->text, -'tag' => $this->tag, -'state' => $this->state, -'input' => $this->input, -'ptr' => $this->ptr, -'unget' => $this->unget, -'verbatim' => $this->verbatim -); -} -function RestoreState($state) { -if (!is_array($state)) return; -$this->token = @$state['token']; -$this->text = @$state['text']; -$this->tag = @$state['tag']; -$this->state = @$state['state']; -$this->input = @$state['input']; -$this->ptr = @$state['ptr']; -$this->unget = @$state['unget']; -$this->verbatim = @$state['verbatim']; -} -function Internal_StripQuotes($string) { -if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) -return $matches[1]; -else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) -return $matches[1]; -else return $string; -} -function Internal_ClassifyPiece($ptr, $pieces) { -if ($ptr >= count($pieces)) return -1; -$piece = $pieces[$ptr]; -if ($piece == '=') return '='; -else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; -else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; -else return 'A'; -} -function Internal_DecodeTag($tag) { -$result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', -'_hasend' => false, '_end' => false, '_default' => false); -$tag = substr($tag, 1, strlen($tag)-2); -$ch = ord(substr($tag, 0, 1)); -if ($ch >= 0 && $ch <= 32) return $result; -$pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", -$tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); -$ptr = 0; -if (count($pieces) < 1) return $result; -if (@substr($pieces[$ptr], 0, 1) == '/') { -$result['_name'] = strtolower(substr($pieces[$ptr++], 1)); -$result['_end'] = true; -} -else { -$result['_name'] = strtolower($pieces[$ptr++]); -$result['_end'] = false; -} -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -$params = Array(); -if ($type != '=') { -$result['_default'] = false; -$params[] = Array('key' => '', 'value' => ''); -} -else { -$ptr++; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type == "\"") -$value = $this->Internal_StripQuotes($pieces[$ptr++]); -else { -$after_space = false; -$start = $ptr; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { -if ($type == ' ') $after_space = true; -if ($type == '=' && $after_space) break; -$ptr++; -} -if ($type == -1) $ptr--; -if ($type == '=') { -$ptr--; -while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') -$ptr--; -while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') -$ptr--; -} -$value = ""; -for (; $start <= $ptr; $start++) { -if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') -$value .= " "; -else $value .= $this->Internal_StripQuotes($pieces[$start]); -} -$value = trim($value); -$ptr++; -} -$result['_default'] = $value; -$params[] = Array('key' => '', 'value' => $value); -} -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { -while ($type == ' ') { -$ptr++; -$type = $this->Internal_ClassifyPiece($ptr, $pieces); -} -if ($type == 'A' || $type == '"') -$key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); -else if ($type == '=') { -$ptr++; -continue; -} -else if ($type == -1) break; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type != '=') -$value = $this->Internal_StripQuotes($key); -else { -$ptr++; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') -$ptr++; -if ($type == '"') { -$value = $this->Internal_StripQuotes($pieces[$ptr++]); -} -else if ($type != -1) { -$value = $pieces[$ptr++]; -while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 -&& $type != ' ') -$value .= $pieces[$ptr++]; -} -else $value = ""; -} -if (substr($key, 0, 1) != '_') -$result[$key] = $value; -$params[] = Array('key' => $key, 'value' => $value); -} -$result['_params'] = $params; -return $result; -} -} - -class BBCodeLibrary { -var $default_smileys = Array( -':)' => 'smile.gif', ':-)' => 'smile.gif', -'=)' => 'smile.gif', '=-)' => 'smile.gif', -':(' => 'frown.gif', ':-(' => 'frown.gif', -'=(' => 'frown.gif', '=-(' => 'frown.gif', -':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', -'=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', -'>:('=> 'angry.gif', '>:-('=> 'angry.gif', -'>=('=> 'angry.gif', '>=-('=> 'angry.gif', -'D:' => 'angry.gif', 'D-:' => 'angry.gif', -'D=' => 'angry.gif', 'D-=' => 'angry.gif', -'>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', -'>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', -'>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', -'>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', -'>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', -'>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', -'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', -'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', -':O' => 'surprise.gif', ':-O' => 'surprise.gif', -'=O' => 'surprise.gif', '=-O' => 'surprise.gif', -':?' => 'confuse.gif', ':-?' => 'confuse.gif', -'=?' => 'confuse.gif', '=-?' => 'confuse.gif', -':s' => 'worry.gif', ':-S' => 'worry.gif', -'=s' => 'worry.gif', '=-S' => 'worry.gif', -':|' => 'neutral.gif', ':-|' => 'neutral.gif', -'=|' => 'neutral.gif', '=-|' => 'neutral.gif', -':I' => 'neutral.gif', ':-I' => 'neutral.gif', -'=I' => 'neutral.gif', '=-I' => 'neutral.gif', -':/' => 'irritated.gif', ':-/' => 'irritated.gif', -'=/' => 'irritated.gif', '=-/' => 'irritated.gif', -':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', -'=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', -':P' => 'tongue.gif', ':-P' => 'tongue.gif', -'=P' => 'tongue.gif', '=-P' => 'tongue.gif', -'X-P' => 'tongue.gif', -'8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', -'B)' => 'cool.gif', 'B-)' => 'cool.gif', -';)' => 'wink.gif', ';-)' => 'wink.gif', -';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', -'^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', -'>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', -'<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', -'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', -';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', -':3' => 'smile3.gif', ':-3' => 'smile3.gif', -'=3' => 'smile3.gif', '=-3' => 'smile3.gif', -';3' => 'wink3.gif', ';-3' => 'wink3.gif', -'' => 'teeth.gif', '' => 'teeth.gif', -'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', -':blue:' => 'blue.gif', -':zzz:' => 'sleepy.gif', -'<3' => 'heart.gif', -':star:' => 'star.gif', -); -var $default_tag_rules = Array( -'b' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'i' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'u' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'s' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'plain_start' => "", -'plain_end' => "", -), -'font' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), -'method' => 'DoFont', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'color' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), -'template' => '{$_content/v}', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'size' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'allow' => Array('_default' => '/^[0-9.]+$/D'), -'method' => 'DoSize', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'sup' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'sub' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'spoiler' => Array( -'simple_start' => "", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'acronym' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'template' => '{$_content/v}', -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -), -'url' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoURL', -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'content' => BBCODE_REQUIRED, -'plain_start' => "", -'plain_end' => "", -'plain_content' => Array('_content', '_default'), -'plain_link' => Array('_default', '_content'), -), -'email' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoEmail', -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'content' => BBCODE_REQUIRED, -'plain_start' => "", -'plain_end' => "", -'plain_content' => Array('_content', '_default'), -'plain_link' => Array('_default', '_content'), -), -'wiki' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoWiki", -'class' => 'link', -'allow_in' => Array('listitem', 'block', 'columns', 'inline'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'plain_start' => "[", -'plain_end' => "]", -'plain_content' => Array('title', '_default'), -'plain_link' => Array('_default', '_content'), -), -'img' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoImage", -'class' => 'image', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'end_tag' => BBCODE_REQUIRED, -'content' => BBCODE_REQUIRED, -'plain_start' => "[image]", -'plain_content' => Array(), -), -'rule' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoRule", -'class' => 'block', -'allow_in' => Array('listitem', 'block', 'columns'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "sns", -'after_tag' => "sns", -'plain_start' => "\n-----\n", -'plain_end' => "", -'plain_content' => Array(), -), -'br' => Array( -'mode' => BBCODE_MODE_SIMPLE, -'simple_start' => "
    \n", -'simple_end' => "", -'class' => 'inline', -'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "s", -'after_tag' => "s", -'plain_start' => "\n", -'plain_end' => "", -'plain_content' => Array(), -), -'left' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'right' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'center' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'indent' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'columns' => Array( -'simple_start' => "\n
    \n", -'simple_end' => "\n
    \n", -'class' => 'columns', -'allow_in' => Array('listitem', 'block', 'columns'), -'end_tag' => BBCODE_REQUIRED, -'content' => BBCODE_REQUIRED, -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'nextcol' => Array( -'simple_start' => "\n\n", -'class' => 'nextcol', -'allow_in' => Array('columns'), -'end_tag' => BBCODE_PROHIBIT, -'content' => BBCODE_PROHIBIT, -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "", -), -'code' => Array( -'mode' => BBCODE_MODE_ENHANCED, -'template' => "\n
    \n
    Code:
    \n
    {\$_content/v}
    \n
    \n", -'class' => 'code', -'allow_in' => Array('listitem', 'block', 'columns'), -'content' => BBCODE_VERBATIM, -'before_tag' => "sns", -'after_tag' => "sn", -'before_endtag' => "sn", -'after_endtag' => "sns", -'plain_start' => "\nCode:\n", -'plain_end' => "\n", -), -'quote' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => "DoQuote", -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\nQuote:\n", -'plain_end' => "\n", -), -'list' => Array( -'mode' => BBCODE_MODE_LIBRARY, -'method' => 'DoList', -'class' => 'list', -'allow_in' => Array('listitem', 'block', 'columns'), -'before_tag' => "sns", -'after_tag' => "sns", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n", -'plain_end' => "\n", -), -'*' => Array( -'simple_start' => "
  • ", -'simple_end' => "
  • \n", -'class' => 'listitem', -'allow_in' => Array('list'), -'end_tag' => BBCODE_OPTIONAL, -'before_tag' => "s", -'after_tag' => "s", -'before_endtag' => "sns", -'after_endtag' => "sns", -'plain_start' => "\n * ", -'plain_end' => "\n", -), -); -function DoURL($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); -if ($bbcode->IsValidURL($url)) { -if ($bbcode->debug) -print "ISVALIDURL
    "; -if ($bbcode->url_targetable !== false && isset($params['target'])) -$target = " target=\"" . htmlspecialchars($params['target']) . "\""; -else $target = ""; -if ($bbcode->url_target !== false) -if (!($bbcode->url_targetable == 'override' && isset($params['target']))) -$target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; -return '' . $content . ''; -} -else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); -} -function DoEmail($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); -if ($bbcode->IsValidEmail($email)) -return '' . $content . ''; -else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); -} -function DoSize($bbcode, $action, $name, $default, $params, $content) { -switch ($default) { -case '0': $size = '.5em'; break; -case '1': $size = '.67em'; break; -case '2': $size = '.83em'; break; -default: -case '3': $size = '1.0em'; break; -case '4': $size = '1.17em'; break; -case '5': $size = '1.5em'; break; -case '6': $size = '2.0em'; break; -case '7': $size = '2.5em'; break; -} -return "$content"; -} -function DoFont($bbcode, $action, $name, $default, $params, $content) { -$fonts = explode(",", $default); -$result = ""; -$special_fonts = Array( -'serif' => 'serif', -'sans-serif' => 'sans-serif', -'sans serif' => 'sans-serif', -'sansserif' => 'sans-serif', -'sans' => 'sans-serif', -'cursive' => 'cursive', -'fantasy' => 'fantasy', -'monospace' => 'monospace', -'mono' => 'monospace', -); -foreach ($fonts as $font) { -$font = trim($font); -if (isset($special_fonts[$font])) { -if (strlen($result) > 0) $result .= ","; -$result .= $special_fonts[$font]; -} -else if (strlen($font) > 0) { -if (strlen($result) > 0) $result .= ","; -$result .= "'$font'"; -} -} -return "$content"; -} -function DoWiki($bbcode, $action, $name, $default, $params, $content) { -$name = $bbcode->Wikify($default); -if ($action == BBCODE_CHECK) -return strlen($name) > 0; -$title = trim(@$params['title']); -if (strlen($title) <= 0) $title = trim($default); -return "wiki_url}$name\" class=\"bbcode_wiki\">" -. htmlspecialchars($title) . ""; -} -function DoImage($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -$content = trim($bbcode->UnHTMLEncode(strip_tags($content))); -if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { -if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { -if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { -$info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); -if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { -return "local_img_url}/{$content}") . "\" alt=\"" -. htmlspecialchars(basename($content)) . "\" width=\"" -. htmlspecialchars($info[0]) . "\" height=\"" -. htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; -} -} -} -else if ($bbcode->IsValidURL($content, false)) { -return "\"""; -} -} -return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); -} -function DoRule($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -else return $bbcode->rule_html; -} -function DoQuote($bbcode, $action, $name, $default, $params, $content) { -if ($action == BBCODE_CHECK) return true; -if (isset($params['name'])) { -$title = htmlspecialchars(trim($params['name'])) . " wrote"; -if (isset($params['date'])) -$title .= " on " . htmlspecialchars(trim($params['date'])); -$title .= ":"; -if (isset($params['url'])) { -$url = trim($params['url']); -if ($bbcode->IsValidURL($url)) -$title = "" . $title . ""; -} -} -else if (!is_string($default)) -$title = "Quote:"; -else $title = htmlspecialchars(trim($default)) . " wrote:"; -return "\n
    \n
    " -. $title . "
    \n
    " -. $content . "
    \n
    \n"; -} -function DoList($bbcode, $action, $name, $default, $params, $content) { -$list_styles = Array( -'1' => 'decimal', -'01' => 'decimal-leading-zero', -'i' => 'lower-roman', -'I' => 'upper-roman', -'a' => 'lower-alpha', -'A' => 'upper-alpha', -); -$ci_list_styles = Array( -'circle' => 'circle', -'disc' => 'disc', -'square' => 'square', -'greek' => 'lower-greek', -'armenian' => 'armenian', -'georgian' => 'georgian', -); -$ul_types = Array( -'circle' => 'circle', -'disc' => 'disc', -'square' => 'square', -); -$default = trim($default); -if ($action == BBCODE_CHECK) { -if (!is_string($default) || strlen($default) == "") return true; -else if (isset($list_styles[$default])) return true; -else if (isset($ci_list_styles[strtolower($default)])) return true; -else return false; -} -if (!is_string($default) || strlen($default) == "") { -$elem = 'ul'; -$type = ''; -} -else if ($default == '1') { -$elem = 'ol'; -$type = ''; -} -else if (isset($list_styles[$default])) { -$elem = 'ol'; -$type = $list_styles[$default]; -} -else { -$default = strtolower($default); -if (isset($ul_types[$default])) { -$elem = 'ul'; -$type = $ul_types[$default]; -} -else if (isset($ci_list_styles[$default])) { -$elem = 'ol'; -$type = $ci_list_styles[$default]; -} -} -if (strlen($type)) -return "\n<$elem class=\"bbcode_list\" style=\"list-style-type:$type\">\n$content\n"; -else return "\n<$elem class=\"bbcode_list\">\n$content\n"; -} -} - -class BBCodeEmailAddressValidator { -function check_email_address($strEmailAddress) { -if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { -return false; -} -$intAtSymbol = strrpos($strEmailAddress, '@'); -if ($intAtSymbol === false) { -return false; -} -$arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); -$arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); -$arrTempAddress[0] = preg_replace('/"[^"]+"/' -,'' -,$arrEmailAddress[0]); -$arrTempAddress[1] = $arrEmailAddress[1]; -$strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; -if (strrpos($strTempAddress, '@') !== false) { -return false; -} -if (!$this->check_local_portion($arrEmailAddress[0])) { -return false; -} -if (!$this->check_domain_portion($arrEmailAddress[1])) { -return false; -} -return true; -} -function check_local_portion($strLocalPortion) { -if (!$this->check_text_length($strLocalPortion, 1, 64)) { -return false; -} -$arrLocalPortion = explode('.', $strLocalPortion); -for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { -if (!preg_match('.^(' -. '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' -. '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' -.'|' -. '("[^\\\"]{0,62}")' -.')$.' -,$arrLocalPortion[$i])) { -return false; -} -} -return true; -} -function check_domain_portion($strDomainPortion) { -if (!$this->check_text_length($strDomainPortion, 1, 255)) { -return false; -} -if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' -.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' -,$strDomainPortion) || -preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' -.'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' -,$strDomainPortion)) { -return true; -} else { -$arrDomainPortion = explode('.', $strDomainPortion); -if (sizeof($arrDomainPortion) < 2) { -return false; -} -for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { -if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { -return false; -} -if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' -.'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { -return false; -} -} -} -return true; -} -function check_text_length($strText, $intMinimum, $intMaximum) { -$intTextLength = strlen($strText); -if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { -return false; -} else { -return true; -} -} -} - -class BBCode { -var $tag_rules; -var $defaults; -var $current_class; -var $root_class; -var $lost_start_tags; -var $start_tags; -var $allow_ampersand; -var $tag_marker; -var $ignore_newlines; -var $plain_mode; -var $detect_urls; -var $url_pattern; -var $output_limit; -var $text_length; -var $was_limited; -var $limit_tail; -var $limit_precision; -var $smiley_dir; -var $smiley_url; -var $smileys; -var $smiley_regex; -var $enable_smileys; -var $wiki_url; -var $local_img_dir; -var $local_img_url; -var $url_targetable; -var $url_target; -var $rule_html; -var $pre_trim; -var $post_trim; -var $debug; -function BBCode() { -$this->defaults = new BBCodeLibrary; -$this->tag_rules = $this->defaults->default_tag_rules; -$this->smileys = $this->defaults->default_smileys; -$this->enable_smileys = true; -$this->smiley_regex = false; -$this->smiley_dir = $this->GetDefaultSmileyDir(); -$this->smiley_url = $this->GetDefaultSmileyURL(); -$this->wiki_url = $this->GetDefaultWikiURL(); -$this->local_img_dir = $this->GetDefaultLocalImgDir(); -$this->local_img_url = $this->GetDefaultLocalImgURL(); -$this->rule_html = $this->GetDefaultRuleHTML(); -$this->pre_trim = ""; -$this->post_trim = ""; -$this->root_class = 'block'; -$this->lost_start_tags = Array(); -$this->start_tags = Array(); -$this->tag_marker = '['; -$this->allow_ampsersand = false; -$this->current_class = $this->root_class; -$this->debug = false; -$this->ignore_newlines = false; -$this->output_limit = 0; -$this->plain_mode = false; -$this->was_limited = false; -$this->limit_tail = "..."; -$this->limit_precision = 0.15; -$this->detect_urls = false; -$this->url_pattern = '{$text/h}'; -$this->url_targetable = false; -$this->url_target = false; -} -function SetPreTrim($trim = "a") { $this->pre_trim = $trim; } -function GetPreTrim() { return $this->pre_trim; } -function SetPostTrim($trim = "a") { $this->post_trim = $trim; } -function GetPostTrim() { return $this->post_trim; } -function SetRoot($class = 'block') { $this->root_class = $class; } -function SetRootInline() { $this->root_class = 'inline'; } -function SetRootBlock() { $this->root_class = 'block'; } -function GetRoot() { return $this->root_class; } -function SetDebug($enable = true) { $this->debug = $enable; } -function GetDebug() { return $this->debug; } -function SetAllowAmpersand($enable = true) { $this->allow_ampersand = $enable; } -function GetAllowAmpersand() { return $this->allow_ampersand; } -function SetTagMarker($marker = '[') { $this->tag_marker = $marker; } -function GetTagMarker() { return $this->tag_marker; } -function SetIgnoreNewlines($ignore = true) { $this->ignore_newlines = $ignore; } -function GetIgnoreNewlines() { return $this->ignore_newlines; } -function SetLimit($limit = 0) { $this->output_limit = $limit; } -function GetLimit() { return $this->output_limit; } -function SetLimitTail($tail = "...") { $this->limit_tail = $tail; } -function GetLimitTail() { return $this->limit_tail; } -function SetLimitPrecision($prec = 0.15) { $this->limit_precision = $prec; } -function GetLimitPrecision() { return $this->limit_precision; } -function WasLimited() { return $this->was_limited; } -function SetPlainMode($enable = true) { $this->plain_mode = $enable; } -function GetPlainMode() { return $this->plain_mode; } -function SetDetectURLs($enable = true) { $this->detect_urls = $enable; } -function GetDetectURLs() { return $this->detect_urls; } -function SetURLPattern($pattern) { $this->url_pattern = $pattern; } -function GetURLPattern() { return $this->url_pattern; } -function SetURLTargetable($enable) { $this->url_targetable = $enable; } -function GetURLTargetable() { return $this->url_targetable; } -function SetURLTarget($target) { $this->url_target = $target; } -function GetURLTarget() { return $this->url_target; } -function AddRule($name, $rule) { $this->tag_rules[$name] = $rule; } -function RemoveRule($name) { unset($this->tag_rules[$name]); } -function GetRule($name) { return isset($this->tag_rules[$name]) -? $this->tag_rules[$name] : false; } -function ClearRules() { $this->tag_rules = Array(); } -function GetDefaultRule($name) { return isset($this->defaults->default_tag_rules[$name]) -? $this->defaults->default_tag_rules[$name] : false; } -function SetDefaultRule($name) { if (isset($this->defaults->default_tag_rules[$name])) -$this->AddRule($name, $this->defaults->default_tag_rules[$name]); -else $this->RemoveRule($name); } -function GetDefaultRules() { return $this->defaults->default_tag_rules; } -function SetDefaultRules() { $this->tag_rules = $this->defaults->default_tag_rules; } -function SetWikiURL($url) { $this->wiki_url = $url; } -function GetWikiURL($url) { return $this->wiki_url; } -function GetDefaultWikiURL() { return '/?page='; } -function SetLocalImgDir($path) { $this->local_img_dir = $path; } -function GetLocalImgDir() { return $this->local_img_dir; } -function GetDefaultLocalImgDir() { return "img"; } -function SetLocalImgURL($path) { $this->local_img_url = $path; } -function GetLocalImgURL() { return $this->local_img_url; } -function GetDefaultLocalImgURL() { return "img"; } -function SetRuleHTML($html) { $this->rule_html = $html; } -function GetRuleHTML() { return $this->rule_html; } -function GetDefaultRuleHTML() { return "\n
    \n"; } -function AddSmiley($code, $image) { $this->smileys[$code] = $image; $this->smiley_regex = false; } -function RemoveSmiley($code) { unset($this->smileys[$code]); $this->smiley_regex = false; } -function GetSmiley($code) { return isset($this->smileys[$code]) -? $this->smileys[$code] : false; } -function ClearSmileys() { $this->smileys = Array(); $this->smiley_regex = false; } -function GetDefaultSmiley($code) { return isset($this->defaults->default_smileys[$code]) -? $this->defaults->default_smileys[$code] : false; } -function SetDefaultSmiley($code) { $this->smileys[$code] = @$this->defaults->default_smileys[$code]; -$this->smiley_regex = false; } -function GetDefaultSmileys() { return $this->defaults->default_smileys; } -function SetDefaultSmileys() { $this->smileys = $this->defaults->default_smileys; -$this->smiley_regex = false; } -function SetSmileyDir($path) { $this->smiley_dir = $path; } -function GetSmileyDir() { return $this->smiley_dir; } -function GetDefaultSmileyDir() { return "smileys"; } -function SetSmileyURL($path) { $this->smiley_url = $path; } -function GetSmileyURL() { return $this->smiley_url; } -function GetDefaultSmileyURL() { return "smileys"; } -function SetEnableSmileys($enable = true) { $this->enable_smileys = $enable; } -function GetEnableSmileys() { return $this->enable_smileys; } -function nl2br($string) { -return preg_replace("/\\x0A|\\x0D|\\x0A\\x0D|\\x0D\\x0A/", "
    \n", $string); -} -function UnHTMLEncode($string) { -if (function_exists("html_entity_decode")) -return html_entity_decode($string); -$string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); -$string = preg_replace('~&#([0-9]+);~e', 'chr("\\1")', $string); -$trans_tbl = get_html_translation_table(HTML_ENTITIES); -$trans_tbl = array_flip($trans_tbl); -return strtr($string, $trans_tbl); -} -function Wikify($string) { -return rawurlencode(str_replace(" ", "_", -trim(preg_replace("/[!?;@#\$%\\^&*<>=+`~\\x00-\\x20_-]+/", " ", $string)))); -} -function IsValidURL($string, $email_too = true) { -if (preg_match("/^ -(?:https?|ftp):\\/\\/ -(?: -(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+ -[a-zA-Z0-9] -(?:[a-zA-Z0-9-]*[a-zA-Z0-9])? -| -\\[ -(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} -(?: -25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]: -(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ -) -\\] -) -(?::[0-9]{1,5})? -(?:[\\/\\?\\#][^\\n\\r]*)? -$/Dx", $string)) return true; -if (preg_match("/^[^:]+([\\/\\\\?#][^\\r\\n]*)?$/D", $string)) -return true; -if ($email_too) -if (substr($string, 0, 7) == "mailto:") -return $this->IsValidEmail(substr($string, 7)); -return false; -} -function IsValidEmail($string) { -$validator = new BBCodeEmailAddressValidator; -return $validator->check_email_address($string); -/* -return preg_match("/^ -(?: -[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+ -(?:\.[a-z0-9\\!\\#\\\$\\%\\&\\'\\*\\+\\/=\\?\\^_`\\{\\|\\}~-]+)* -| -\"(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])*\" -) -@ -(?: -(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+ -[a-z0-9] -(?:[a-z0-9-]*[a-z0-9])? -| -\\[ -(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.) {3} -(?: -25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]: -(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21-\\x5A\\x53-\\x7F] -|\\\\[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F])+ -) -\\] -) -$/Dx", $string); -*/ -} -function HTMLEncode($string) { -if (!$this->allow_ampersand) -return htmlspecialchars($string); -else return str_replace(Array('<', '>', '"'), -Array('<', '>', '"'), $string); -} -function FixupOutput($string) { -if (!$this->detect_urls) { -$output = $this->Internal_ProcessSmileys($string); -} -else { -$chunks = $this->Internal_AutoDetectURLs($string); -$output = Array(); -if (count($chunks)) { -$is_a_url = false; -foreach ($chunks as $index => $chunk) { -if (!$is_a_url) { -$chunk = $this->Internal_ProcessSmileys($chunk); -} -$output[] = $chunk; -$is_a_url = !$is_a_url; -} -} -$output = implode("", $output); -} -return $output; -} -function Internal_ProcessSmileys($string) { -if (!$this->enable_smileys || $this->plain_mode) { -$output = $this->HTMLEncode($string); -} -else { -if ($this->smiley_regex === false) { -$this->Internal_RebuildSmileys(); -} -$tokens = preg_split($this->smiley_regex, $string, -1, PREG_SPLIT_DELIM_CAPTURE); -if (count($tokens) <= 1) { -$output = $this->HTMLEncode($string); -} -else { -$output = ""; -$is_a_smiley = false; -foreach ($tokens as $token) { -if (!$is_a_smiley) { -$output .= $this->HTMLEncode($token); -} -else { -if (isset($this->smiley_info[$token])) { -$info = $this->smiley_info[$token]; -} -else { -$info = @getimagesize($this->smiley_dir . '/' . $this->smileys[$token]); -$this->smiley_info[$token] = $info; -} -$alt = htmlspecialchars($token); -$output .= "smiley_url . '/' . $this->smileys[$token]) -. "\" width=\"{$info[0]}\" height=\"{$info[1]}\"" -. " alt=\"$alt\" title=\"$alt\" class=\"bbcode_smiley\" />"; -} -$is_a_smiley = !$is_a_smiley; -} -} -} -return $output; -} -function Internal_RebuildSmileys() { -$regex = Array("/(?smileys as $code => $filename) { -if (!$first) $regex[] = "|"; -$regex[] = preg_quote("$code", '/'); -$first = false; -} -$regex[] = ")(?![\\w])/"; -$this->smiley_regex = implode("", $regex); -} -function Internal_AutoDetectURLs($string) { -$output = preg_split("/( (?: -(?:https?|ftp) : \\/* -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -| (?: [0-9A-Fa-f:]+ : [0-9A-Fa-f]{1,4} ) -) -(?: : [0-9]+ )? -(?! [a-zA-Z0-9.:-] ) -(?: -\\/ -[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* -)? -(?: -[?#] -[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ -)? -) | (?: -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -) -(?: : [0-9]+ )? -(?! [a-zA-Z0-9.:-] ) -(?: -\\/ -[^&?#\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]* -)? -(?: -[?#] -[^\\(\\)\\[\\]\\{\\}<>\\'\\\"\\x00-\\x20\\x7F-\\xFF]+ -)? -) | (?: -[a-zA-Z0-9._-]{2,} @ -(?: -(?: (?: [a-zA-Z0-9-]{2,} \\. )+ -(?: arpa | com | org | net | edu | gov | mil | int | [a-z]{2} -| aero | biz | coop | info | museum | name | pro -| example | invalid | localhost | test | local | onion | swift)) -| (?: [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} \\. [0-9]{1,3} ) -) -))/Dx", $string, -1, PREG_SPLIT_DELIM_CAPTURE); -if (count($output) > 1) { -$is_a_url = false; -foreach ($output as $index => $token) { -if ($is_a_url) { -if (preg_match("/^[a-zA-Z0-9._-]{2,}@/", $token)) { -$url = "mailto:" . $token; -} -else if (preg_match("/^(https?:|ftp:)\\/*([^\\/&?#]+)\\/*(.*)\$/", $token, $matches)) { -$url = $matches[1] . '/' . '/' . $matches[2] . "/" . $matches[3]; -} -else { -preg_match("/^([^\\/&?#]+)\\/*(.*)\$/", $token, $matches); -$url = "http:/" . "/" . $matches[1] . "/" . $matches[2]; -} -$params = @parse_url($url); -if (!is_array($params)) $params = Array(); -$params['url'] = $url; -$params['link'] = $url; -$params['text'] = $token; -$output[$index] = $this->FillTemplate($this->url_pattern, $params); -} -$is_a_url = !$is_a_url; -} -} -return $output; -} -function FillTemplate($template, $insert_array, $default_array = Array()) { -$pieces = preg_split('/(\{\$[a-zA-Z0-9_.:\/-]+\})/', $template, --1, PREG_SPLIT_DELIM_CAPTURE); -if (count($pieces) <= 1) -return $template; -$result = Array(); -$is_an_insert = false; -foreach ($pieces as $piece) { -if (!$is_an_insert) { -$result[] = $piece; -} -else if (!preg_match('/\{\$([a-zA-Z0-9_:-]+)((?:\\.[a-zA-Z0-9_:-]+)*)(?:\/([a-zA-Z0-9_:-]+))?\}/', $piece, $matches)) { -$result[] = $piece; -} -else { -if (isset($insert_array[$matches[1]])) -$value = @$insert_array[$matches[1]]; -else $value = @$default_array[$matches[1]]; -if (strlen(@$matches[2])) { -foreach (split(".", substr($matches[2], 1)) as $index) { -if (is_array($value)) -$value = @$value[$index]; -else if (is_object($value)) { -$value = (array)$value; -$value = @$value[$index]; -} -else $value = ""; -} -} -switch (gettype($value)) { -case 'boolean': $value = $value ? "true" : "false"; break; -case 'integer': $value = (string)$value; break; -case 'double': $value = (string)$value; break; -case 'string': break; -default: $value = ""; break; -} -if (strlen(@$matches[3])) -$flags = array_flip(str_split($matches[3])); -else $flags = Array(); -if (!isset($flags['v'])) { -if (isset($flags['w'])) -$value = preg_replace("/[\\x00-\\x09\\x0B-\x0C\x0E-\\x20]+/", " ", $value); -if (isset($flags['t'])) $value = trim($value); -if (isset($flags['b'])) $value = basename($value); -if (isset($flags['e'])) $value = $this->HTMLEncode($value); -else if (isset($flags['k'])) $value = $this->Wikify($value); -else if (isset($flags['h'])) $value = htmlspecialchars($value); -else if (isset($flags['u'])) $value = urlencode($value); -if (isset($flags['n'])) $value = $this->nl2br($value); -} -$result[] = $value; -} -$is_an_insert = !$is_an_insert; -} -return implode("", $result); -} -function Internal_CollectText($array, $start = 0) { -ob_start(); -for ($start = intval($start), $end = count($array); $start < $end; $start++) -print $array[$start][BBCODE_STACK_TEXT]; -$output = ob_get_contents(); -ob_end_clean(); -return $output; -} -function Internal_CollectTextReverse($array, $start = 0, $end = 0) { -ob_start(); -for ($start = intval($start); $start >= $end; $start--) -print $array[$start][BBCODE_STACK_TEXT]; -$output = ob_get_contents(); -ob_end_clean(); -return $output; -} -function Internal_GenerateOutput($pos) { -$output = Array(); -while (count($this->stack) > $pos) { -$token = array_pop($this->stack); -if ($token[BBCODE_STACK_TOKEN] != BBCODE_TAG) { -$output[] = $token; -} -else { -$name = @$token[BBCODE_STACK_TAG]['_name']; -$rule = @$this->tag_rules[$name]; -$end_tag = @$rule['end_tag']; -if (!isset($rule['end_tag'])) $end_tag = BBCODE_REQUIRED; -else $end_tag = $rule['end_tag']; -array_pop($this->start_tags[$name]); -if ($end_tag == BBCODE_PROHIBIT) { -$output[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TAG => false, -BBCODE_STACK_TEXT => $token[BBCODE_STACK_TEXT], -BBCODE_STACK_CLASS => $this->current_class, -); -} -else { -if ($end_tag == BBCODE_REQUIRED) -@$this->lost_start_tags[$name] += 1; -$end = $this->Internal_CleanupWSByIteratingPointer(@$rule['before_endtag'], 0, $output); -$this->Internal_CleanupWSByPoppingStack(@$rule['after_tag'], $output); -$tag_body = $this->Internal_CollectTextReverse($output, count($output)-1, $end); -$this->Internal_CleanupWSByPoppingStack(@$rule['before_tag'], $this->stack); -$this->Internal_UpdateParamsForMissingEndTag(@$token[BBCODE_STACK_TAG]); -$tag_output = $this->DoTag(BBCODE_OUTPUT, $name, -@$token[BBCODE_STACK_TAG]['_default'], @$token[BBCODE_STACK_TAG], $tag_body); -$output = Array(Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TAG => false, -BBCODE_STACK_TEXT => $tag_output, -BBCODE_STACK_CLASS => $this->current_class -)); -} -} -} -$this->Internal_ComputeCurrentClass(); -return $output; -} -function Internal_RewindToClass($class_list) { -$pos = count($this->stack) - 1; -while ($pos >= 0 && !in_array($this->stack[$pos][BBCODE_STACK_CLASS], $class_list)) -$pos--; -if ($pos < 0) { -if (!in_array($this->root_class, $class_list)) -return false; -} -$output = $this->Internal_GenerateOutput($pos+1); -while (count($output)) { -$token = array_pop($output); -$token[BBCODE_STACK_CLASS] = $this->current_class; -$this->stack[] = $token; -} -return true; -} -function Internal_FinishTag($tag_name) { -if (strlen($tag_name) <= 0) -return false; -if (isset($this->start_tags[$tag_name]) -&& count($this->start_tags[$tag_name])) -$pos = array_pop($this->start_tags[$tag_name]); -else $pos = -1; -if ($pos < 0) return false; -$newpos = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['after_tag'], -$pos+1, $this->stack); -$delta = $newpos - ($pos+1); -$output = $this->Internal_GenerateOutput($newpos); -$newend = $this->Internal_CleanupWSByIteratingPointer(@$this->tag_rules[$tag_name]['before_endtag'], -0, $output); -$output = $this->Internal_CollectTextReverse($output, count($output) - 1, $newend); -while ($delta-- > 0) -array_pop($this->stack); -$this->Internal_ComputeCurrentClass(); -return $output; -} -function Internal_ComputeCurrentClass() { -if (count($this->stack) > 0) -$this->current_class = $this->stack[count($this->stack)-1][BBCODE_STACK_CLASS]; -else $this->current_class = $this->root_class; -} -function Internal_DumpStack($array = false, $raw = false) { -if (!$raw) $string = ""; -else $string = ""; -if ($array === false) -$array = $this->stack; -foreach ($array as $item) { -switch (@$item[BBCODE_STACK_TOKEN]) { -case BBCODE_TEXT: -$string .= "\"" . htmlspecialchars(@$item[BBCODE_STACK_TEXT]) . "\" "; -break; -case BBCODE_WS: -$string .= "WS "; -break; -case BBCODE_NL: -$string .= "NL "; -break; -case BBCODE_TAG: -$string .= "[" . htmlspecialchars(@$item[BBCODE_STACK_TAG]['_name']) . "] "; -break; -default: -$string .= "unknown "; -break; -} -} -if (!$raw) $string .= ""; -return $string; -} -function Internal_CleanupWSByPoppingStack($pattern, &$array) { -if (strlen($pattern) <= 0) return; -$oldlen = count($array); -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -while (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_WS) -array_pop($array); -break; -case 'n': -if (count($array) > 0 && $array[count($array)-1][BBCODE_STACK_TOKEN] == BBCODE_NL) -array_pop($array); -break; -case 'a': -while (count($array) > 0 -&& (($token = $array[count($array)-1][BBCODE_STACK_TOKEN]) == BBCODE_WS -|| $token == BBCODE_NL)) -array_pop($array); -break; -} -} -if (count($array) != $oldlen) { -$this->Internal_ComputeCurrentClass(); -} -} -function Internal_CleanupWSByEatingInput($pattern) { -if (strlen($pattern) <= 0) return; -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -$token_type = $this->lexer->NextToken(); -while ($token_type == BBCODE_WS) { -$token_type = $this->lexer->NextToken(); -} -$this->lexer->UngetToken(); -break; -case 'n': -$token_type = $this->lexer->NextToken(); -if ($token_type != BBCODE_NL) -$this->lexer->UngetToken(); -break; -case 'a': -$token_type = $this->lexer->NextToken(); -while ($token_type == BBCODE_WS || $token_type == BBCODE_NL) { -$token_type = $this->lexer->NextToken(); -} -$this->lexer->UngetToken(); -break; -} -} -} -function Internal_CleanupWSByIteratingPointer($pattern, $pos, $array) { -if (strlen($pattern) <= 0) return $pos; -foreach (str_split($pattern) as $char) { -switch ($char) { -case 's': -while ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_WS) -$pos++; -break; -case 'n': -if ($pos < count($array) && $array[$pos][BBCODE_STACK_TOKEN] == BBCODE_NL) -$pos++; -break; -case 'a': -while ($pos < count($array) -&& (($token = $array[$pos][BBCODE_STACK_TOKEN]) == BBCODE_WS || $token == BBCODE_NL)) -$pos++; -break; -} -} -return $pos; -} -function Internal_LimitText($string, $limit) { -$chunks = preg_split("/([\\x00-\\x20]+)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE); -$output = ""; -foreach ($chunks as $chunk) { -if (strlen($output) + strlen($chunk) > $limit) -break; -$output .= $chunk; -} -$output = rtrim($output); -return $output; -} -function Internal_DoLimit() { -$this->Internal_CleanupWSByPoppingStack("a", $this->stack); -if (strlen($this->limit_tail) > 0) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->limit_tail, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->was_limited = true; -} -function DoTag($action, $tag_name, $default_value, $params, $contents) { -$tag_rule = @$this->tag_rules[$tag_name]; -switch ($action) { -case BBCODE_CHECK: -if (isset($tag_rule['allow'])) { -foreach ($tag_rule['allow'] as $param => $pattern) { -if ($param == '_content') $value = $contents; -else if ($param == '_defaultcontent') { -if (strlen($default_value)) -$value = $default_value; -else $value = $contents; -} -else { -if (isset($params[$param])) -$value = $params[$param]; -else $value = @$tag_rule['default'][$param]; -} -if (!preg_match($pattern, $value)) { -return false; -} -} -return true; -} -switch (@$tag_rule['mode']) { -default: -case BBCODE_MODE_SIMPLE: -$result = true; -break; -case BBCODE_MODE_ENHANCED: -$result = true; -break; -case BBCODE_MODE_INTERNAL: -$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_LIBRARY: -$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_CALLBACK: -$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_CHECK, -$tag_name, $default_value, $params, $contents); -break; -} -return $result; -case BBCODE_OUTPUT: -if ($this->plain_mode) { -if (!isset($tag_rule['plain_content'])) -$plain_content = Array('_content'); -else $plain_content = $tag_rule['plain_content']; -$result = $possible_content = ""; -foreach ($plain_content as $possible_content) { -if ($possible_content == '_content' -&& strlen($contents) > 0) { -$result = $contents; -break; -} -if (isset($params[$possible_content]) -&& strlen($params[$possible_content]) > 0) { -$result = htmlspecialchars($params[$possible_content]); -break; -} -} -$start = @$tag_rule['plain_start']; -$end = @$tag_rule['plain_end']; -if (isset($tag_rule['plain_link'])) { -$link = $possible_content = ""; -foreach ($tag_rule['plain_link'] as $possible_content) { -if ($possible_content == '_content' -&& strlen($contents) > 0) { -$link = $this->UnHTMLEncode(strip_tags($contents)); -break; -} -if (isset($params[$possible_content]) -&& strlen($params[$possible_content]) > 0) { -$link = $params[$possible_content]; -break; -} -} -$params = @parse_url($link); -if (!is_array($params)) $params = Array(); -$params['link'] = $link; -$params['url'] = $link; -$start = $this->FillTemplate($start, $params); -$end = $this->FillTemplate($end, $params); -} -return $start . $result . $end; -} -switch (@$tag_rule['mode']) { -default: -case BBCODE_MODE_SIMPLE: -$result = @$tag_rule['simple_start'] . $contents . @$tag_rule['simple_end']; -break; -case BBCODE_MODE_ENHANCED: -$result = $this->Internal_DoEnhancedTag($tag_rule, $params, $contents); -break; -case BBCODE_MODE_INTERNAL: -$result = @call_user_func(Array($this, @$tag_rule['method']), BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_LIBRARY: -$result = @call_user_func(Array($this->defaults, @$tag_rule['method']), $this, BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -case BBCODE_MODE_CALLBACK: -$result = @call_user_func(@$tag_rule['method'], $this, BBCODE_OUTPUT, -$tag_name, $default_value, $params, $contents); -break; -} -return $result; -default: -return false; -} -} -function Internal_DoEnhancedTag($tag_rule, $params, $contents) { -$params['_content'] = $contents; -$params['_defaultcontent'] = strlen(@$params['_default']) ? $params['_default'] : $contents; -return $this->FillTemplate(@$tag_rule['template'], $params, @$tag_rule['default']); -} -function Internal_UpdateParamsForMissingEndTag(&$params) { -switch ($this->tag_marker) { -case '[': $tail_marker = ']'; break; -case '<': $tail_marker = '>'; break; -case '{': $tail_marker = '}'; break; -case '(': $tail_marker = ')'; break; -default: $tail_marker = $this->tag_marker; break; -} -$params['_endtag'] = $this->tag_marker . '/' . $params['_name'] . $tail_marker; -} -function Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule) { -if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$tag_params['_default'], $tag_params, ""); -$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_tag']); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule) { -$state = $this->lexer->SaveState(); -$end_tag = $this->lexer->tagmarker . "/" . $tag_name . $this->lexer->end_tagmarker; -$start = count($this->stack); -$this->lexer->verbatim = true; -while (($token_type = $this->lexer->NextToken()) != BBCODE_EOI) { -if ($this->lexer->text == $end_tag) { -$end_tag_params = $this->lexer->tag; -break; -} -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$text = $this->Internal_LimitText($this->lexer->text, -$this->output_limit - $this->text_length); -if (strlen($text) > 0) { -$this->text_length += strlen($text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->Internal_DoLimit(); -break; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => $token_type, -BBCODE_STACK_TEXT => htmlspecialchars($this->lexer->text), -BBCODE_STACK_TAG => $this->lexer->tag, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->lexer->verbatim = false; -if ($token_type == BBCODE_EOI) { -$this->lexer->RestoreState($state); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$newstart = $this->Internal_CleanupWSByIteratingPointer(@$tag_rule['after_tag'], $start, $this->stack); -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_endtag'], $this->stack); -$this->Internal_CleanupWSByEatingInput(@$tag_rule['after_endtag']); -$content = $this->Internal_CollectText($this->stack, $newstart); -array_splice($this->stack, $start); -$this->Internal_ComputeCurrentClass(); -$this->Internal_CleanupWSByPoppingStack(@$tag_rule['before_tag'], $this->stack); -$tag_params['_endtag'] = $end_tag_params['_tag']; -$tag_params['_hasend'] = true; -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, -@$tag_params['_default'], $tag_params, $content); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Internal_ParseStartTagToken() { -$tag_params = $this->lexer->tag; -$tag_name = @$tag_params['_name']; -if (!isset($this->tag_rules[$tag_name])) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -$tag_rule = $this->tag_rules[$tag_name]; -$allow_in = is_array($tag_rule['allow_in']) -? $tag_rule['allow_in'] : Array($this->root_class); -if (!in_array($this->current_class, $allow_in)) { -if (!$this->Internal_RewindToClass($allow_in)) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -} -$end_tag = isset($tag_rule['end_tag']) ? $tag_rule['end_tag'] : BBCODE_REQUIRED; -if ($end_tag == BBCODE_PROHIBIT) { -$this->Internal_ProcessIsolatedTag($tag_name, $tag_params, $tag_rule); -return; -} -if (!$this->DoTag(BBCODE_CHECK, $tag_name, @$tag_params['_default'], $tag_params, "")) { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -return; -} -if (@$tag_rule['content'] == BBCODE_VERBATIM) { -$this->Internal_ProcessVerbatimTag($tag_name, $tag_params, $tag_rule); -return; -} -if (isset($tag_rule['class'])) -$newclass = $tag_rule['class']; -else $newclass = $this->root_class; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => $this->lexer->token, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => $this->lexer->tag, -BBCODE_STACK_CLASS => ($this->current_class = $newclass), -); -if (!isset($this->start_tags[$tag_name])) -$this->start_tags[$tag_name] = Array(count($this->stack)-1); -else $this->start_tags[$tag_name][] = count($this->stack)-1; -} -function Internal_ParseEndTagToken() { -$tag_params = $this->lexer->tag; -$tag_name = @$tag_params['_name']; -$contents = $this->Internal_FinishTag($tag_name); -if ($contents === false) { -if (@$this->lost_start_tags[$tag_name] > 0) { -$this->lost_start_tags[$tag_name]--; -} -else { -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -return; -} -$start_tag_node = array_pop($this->stack); -$start_tag_params = $start_tag_node[BBCODE_STACK_TAG]; -$this->Internal_ComputeCurrentClass(); -$this->Internal_CleanupWSByPoppingStack(@$this->tag_rules[$tag_name]['before_tag'], $this->stack); -$start_tag_params['_endtag'] = $tag_params['_tag']; -$start_tag_params['_hasend'] = true; -$output = $this->DoTag(BBCODE_OUTPUT, $tag_name, @$start_tag_params['_default'], -$start_tag_params, $contents); -$this->Internal_CleanupWSByEatingInput(@$this->tag_rules[$tag_name]['after_endtag']); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $output, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -function Parse($string) { -$this->lexer = new BBCodeLexer($string, $this->tag_marker); -$this->lexer->debug = $this->debug; -$old_output_limit = $this->output_limit; -if ($this->output_limit > 0) { -if (strlen($string) < $this->output_limit) { -$this->output_limit = 0; -} -else if ($this->limit_precision > 0) { -$guess_length = $this->lexer->GuessTextLength(); -if ($guess_length < $this->output_limit * ($this->limit_precision + 1.0)) { -$this->output_limit = 0; -} -else { -} -} -} -$this->stack = Array(); -$this->start_tags = Array(); -$this->lost_start_tags = Array(); -$this->text_length = 0; -$this->was_limited = false; -if (strlen($this->pre_trim) > 0) -$this->Internal_CleanupWSByEatingInput($this->pre_trim); -$newline = $this->plain_mode ? "\n" : "
    \n"; -while (true) { -if (($token_type = $this->lexer->NextToken()) == BBCODE_EOI) { -break; -} -switch ($token_type) { -case BBCODE_TEXT: -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$text = $this->Internal_LimitText($this->lexer->text, -$this->output_limit - $this->text_length); -if (strlen($text) > 0) { -$this->text_length += strlen($text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_TEXT, -BBCODE_STACK_TEXT => $this->FixupOutput($this->lexer->text), -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -break; -case BBCODE_WS: -if ($this->output_limit > 0 -&& $this->text_length + strlen($this->lexer->text) >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += strlen($this->lexer->text); -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_WS, -BBCODE_STACK_TEXT => $this->lexer->text, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -break; -case BBCODE_NL: -if ($this->ignore_newlines) { -if ($this->output_limit > 0 -&& $this->text_length + 1 >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += 1; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_WS, -BBCODE_STACK_TEXT => "\n", -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -} -else { -$this->Internal_CleanupWSByPoppingStack("s", $this->stack); -if ($this->output_limit > 0 -&& $this->text_length + 1 >= $this->output_limit) { -$this->Internal_DoLimit(); -break 2; -} -$this->text_length += 1; -$this->stack[] = Array( -BBCODE_STACK_TOKEN => BBCODE_NL, -BBCODE_STACK_TEXT => $newline, -BBCODE_STACK_TAG => false, -BBCODE_STACK_CLASS => $this->current_class, -); -$this->Internal_CleanupWSByEatingInput("s"); -} -break; -case BBCODE_TAG: -$this->Internal_ParseStartTagToken(); -break; -case BBCODE_ENDTAG: -$this->Internal_ParseEndTagToken(); -break; -default: -break; -} -} -if (strlen($this->post_trim) > 0) -$this->Internal_CleanupWSByPoppingStack($this->post_trim, $this->stack); -$result = $this->Internal_GenerateOutput(0); -$result = $this->Internal_CollectTextReverse($result, count($result) - 1); -$this->output_limit = $old_output_limit; -if ($this->plain_mode) { -$result = preg_replace("/[\\x00-\\x09\\x0B-\\x20]+/", " ", $result); -$result = preg_replace("/(?:[\\x20]*\\n) {2,}[\\x20]*/", "\n\n", $result); -$result = trim($result); -} -return $result; -} -} \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/powered_by_nbbc.png b/app/Plugin/comment/Lib/nbbc/powered_by_nbbc.png deleted file mode 100644 index 0e45f18675fdd5c26ac56a4c3a1d4b555fe5c023..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1821 zcmV+&2jcjNP)l^rLI zCnl37B%35Hn=LGsEHsKVJB2$ln=>|;HYTAaG@~>#sxv&TJUpd5II}oSflW?~PDX@A zNtQ`dgHu$CR9=Q&TZ3C$jayrgTV0Y}SCv;xq)bJ%MMbOHnZkcXvkZp65b8?z;dzO28ntFYmeS4jIYo=>zvub^$eRG_1c)WN_!%R}f zQd7)RTg_Wn(pOv5TVL5%X@0%YJ#AGjH8T=tc`=JgO0O~ zjJ}M7wS=6loRg`Ok+hMOww05!la{-doVJ{qyqTN4o2Ij-q_m`?yQ8MQrmDTFvAeOe zy|aqfijv2Ykin3X$di=Gl$ykvoy?t&%#WDmn4`g?rOu_H%b}{ms;=2-P*zC!OZE*&GOCL;@jTg-reNg+3wlc z_t@g);^yY&=jZ3;=H=kx;O6P(;OXG#=;-O`>FVn0>+9?3>gnw4?CtIC?(XjI@9*mF z>g@9D?ECEI`R4HO@bU5S^Yioa^78cb^!E1l_xJbm|MK|w`1<<#`}_O+{QUm@{{R2~ zWx8C$00001bW%=J06^y0W&i*H32;bRa{vGi!vFvd!vV){sAK>D010$OSad^gZEa<4 zbN~Q}DvbsJ00d!4L_t(|oUM2#pLU+AMtIOsk_52paa-=ok4i!Su(bo4S5A{Xe@znI3xe~V&}u7fC|I!JdE z=n`(h+Y!dG@n8Q3gM-l^Mg7ZS8oO==gRw_2s8L0W4m+ve!q>^d~$-Xg4rnOJmOaSs{}_UIIrcy zN*MtT>rOsY@Vbs>xjy)$qiyQ}-drnK*Q@>h{kh#SBr;%QK8R-Pg&X#owT+D;D@+wb zOP~tZHY$9UJ8ve`aJ8`}^cmaUv@L?6y>+9=2D+2;bGy&yezB!NqOqXx>G~`ZD(dSO zOe_~j+&dO1oHP~rd>qrH0t!u&wLag?g`a@keskgO40>u~H3Q-7`qPk*g7mCCbHlDE zO{^aoVC_*9x6}e=;lMx-;R_nQ+2kQN*8=96#-{82{L<1Y5)7QC#xgDYyIV?-3z`~vhY{SP^F1hp zgxynmT9`FM=8NwY^AEY{NGKAgr52}s)ejHZ5^-vJNh)Izmz((8pnGyx$2>6+3z zVJ~2|qq#k`>~e6TO)j}owMh`m8CQ%1;nYuc zp@$)1=fqpDTu4cYVSJICl)njYXurIC zIW9jTCM5s3<*6D)srsAwnv+I8#wAqudpz5Yt|uzA1j0Q?%TQ~J%Di_P``f+$WXH7)n_1P;I@E$T8mw;{ z^5|iwN@d?(IWd0004iP)t-s{{a6j zd@VG4G%s~8NPb8fh#DJ`8!Cz_K8!v)i90oiH93onKy_URax0Nu)_ftVcw&L`}I(Sg2THmtu37b9S6|dYyV;hhSl#VPm6X zWTj+fp=D;MW@xKuYOZQ;o^NZhYi+b`X0&E~qJ4X#dvK?4ak+7HzjS!8ct^xXQq59R z$5U3?R$Ad&c*b~q%zSdna(mNzV%B19^=)J5V}PcBfun(itA&iLjE|*{hq#B8t(BCr zl$^Ajp1Gc(vZ1B5rK7r|sky1Fy{v@6goN9Kn8=u%*_@Z=m#M<3rPZaQ)1#`}s;Akf zv%#~w#=E%PxU|%?u%+;<>uw$=_>>gww4?d|UF?(6I8?)2{O_V4fa@9*&M$Wm_FwOSg?f6*2Y!>eGxwc2_k-y6jtH+X8u;pUO zs2({zjmGYD{V@v5DWf;xqM>k-)7iTxFn+?k-Aveqs<-r2IWZ#m#FY+Zhmy9 zQg%tIx^_(#b54?Iw|m%PDvFY8H!HosQXpQUf2EDQ@(&JC6t7uP6h=sKc5iytE{fHu zU@S5{MT^DYG`SgcaiZ%zDZap7gFxVN8(GdQ%TOI`7-NdX7}-h8Paf@zVO)0a(0Jk~ zjM*JGk6oWgFspEe;~Zuwm=L(wMuy7Ba>p;)1VNw}kdcHSlpT9xg79du(t8QQhV&4`KzBU7L;ME);8PJTnVeYw0000c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddvl*m0L^u{KIM~P`Eh18|;2|R;t1y#5!-7Re zy9C(%Y#0<6npijmSQtJCFgP;vx@!eo*r?>*$`qg!u`}V|sR^o#A`>DD7@M0cct!41 zGB}=Rbr6&hSQVVc#A=$$CfoJNp=qM5s6~oa!wSb{Ufz@|kqob|Gq7>ZXjy4?fPtBf jtw(3V#03mYte;*j-5vJWg{gtl;f?l3?T2iPj11NQMmA6W diff --git a/app/Plugin/comment/Lib/nbbc/smileys/anime.gif b/app/Plugin/comment/Lib/nbbc/smileys/anime.gif deleted file mode 100644 index 24df9493ddb4e7538039a97b252f2633e0a66e95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodda~Y&LL^u{KIM~P`Eh18|;2|R;t1weS$AU#i z+ZovWY#J0AkN0yJ2tAp>(9krQi$R5BW}z|@12cn81cR`HM+XCsOvB0qhXw}bDkh7B z1qqDI%vRrIHaIvoF}n1=3hBC(d14Vm-@7+IHl{eQVcK$Njl^edmsZv-Y#I^|vKxE2 l4$GN3Y|m@l%)%<*aoSuru2-0oL#@GBF`>DDosEUT8UR2#Qy>5U diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigeyes.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigeyes.gif deleted file mode 100644 index a281b89221c0778a7d931fbbeab3340ed3cc1412..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmZ?wbhEHb6krfw*vtR|_4V~L(~Qrg89&&-@b8l9zw7${?pXeN==}eg@Bi1~|34)D zXZQ~UV6_a2KUp{#7?!LBsoMl7A!c}$RQ~r)389XiIG*9DWOB5(Y2eO zk!MbagM%Y07Z=wFj}HwE47^ezK9UJbj&-s#te9dMoa)BFmUL|mCxfHU9JRoAIWIRS zoa8r@Y-e!U!YL}%(_x^%U=09hT2xj5 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigsmile.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigsmile.gif deleted file mode 100644 index a820c71212bac2b1c08f18e94d1186f3071a74c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd6Bwj9L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+R&*lI|6B{jt^oA7-Ofm)xfr*TBBDRZNSg~Q%)h0$B35^R=7celz YKd#H&U8vN!n?+%g?+SygLk$ep0JJzy_y7O^ diff --git a/app/Plugin/comment/Lib/nbbc/smileys/bigwink.gif b/app/Plugin/comment/Lib/nbbc/smileys/bigwink.gif deleted file mode 100644 index 84ee7483a1a5cac63f6746b184c3dcab4562fdf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmZ?wbhEHb6krfw*vtR|Gt-REq#6Iar26l={=Ykx{~kL3f9CuDb@=}eiT}T5|Nm9` zpW#1PA&5}?$->FNz{8*e;(*Lx;OJ$L;2^9;Hj7SKCO9l* zoW@W$&85=lfY*F;UN2UG2QLoIXI#dV5uuQLX@2m|Zz>uM3}LHSFA1ISC~LU6J?&=J T9E%0-8fI}m_wtnxV6X-NM&?i2 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/blue.gif b/app/Plugin/comment/Lib/nbbc/smileys/blue.gif deleted file mode 100644 index 174db4983c268d2db7263de4998561512e3d8d6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 zcmZ?wbhEHb6krfw*vtR|P8n1GGyG5O{FmMHudx4L`NV%UQ~x*5_}@AE|Acw}r!V{u zRs|&#f3k2gFfcLbfcPLY7&xXeuyTlSELd=`k%Lu)Ct<$1_s7nF6ItXjs*?TObMK95(?Ya Y-Pj?m&UeF_LFrZt*Uf*T5&{g?0B9Obng9R* diff --git a/app/Plugin/comment/Lib/nbbc/smileys/boggle.gif b/app/Plugin/comment/Lib/nbbc/smileys/boggle.gif deleted file mode 100644 index 55b3da12aeba9a049483b4a1ed9ba690114fa06f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmZ?wbhEHb6krfw*vtR|_4V~<(v1IIQvG*b|KA+Z`fq|Pr2gC)L!N4(@fsun{LxO^1GdGt}g24vI=0(7i&icBnu{Cp-8f*zc1WMtt}2nb4GVpZoAQCKi} z`N`P~d>pD=3F(cK^aB^woHA($o)xrQ=tN|}frfd;?R*Lm7ql)iu<$%lF=)s>#=x}a a)Yn&L4_$UJeR`&neSMwd#4=_k25SITqEqbv diff --git a/app/Plugin/comment/Lib/nbbc/smileys/cool.gif b/app/Plugin/comment/Lib/nbbc/smileys/cool.gif deleted file mode 100644 index 35e35a8c081663328cb44398602e8be6932e11b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddZ4A;JA{+}A9Bkx}77-~}@Q{&_RhTKEf}zp1 zo1c+q&J6*_rha+B6qOejluxiI@n@Kv*pzyjfiY%NOXP+I#+hvVcUTgFQyp2kc)IS~ z-2CVy3tu_ciVnZ~O Oz;?Be8%Bu^4Aub7>`v7H diff --git a/app/Plugin/comment/Lib/nbbc/smileys/evil.gif b/app/Plugin/comment/Lib/nbbc/smileys/evil.gif deleted file mode 100644 index 63073dd9935a3d5036d37dfa1d588f0eeed54682..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmZ?wbhEHb6krfw*vtR|K0ZD((~Qrg8UMSa`tQ2_zdM%y9yLxf|&f`g45(jqbi3m!HyvI;XvG%Q$j zv`c{9&xXN)p^1f4fP>+K0D~hlue(;jg^fz?t;_*R5jztOo|>S_C^8|kfU&vBf>-2D zC4=L6RtG^DfmOk2OsuB4Y_eUK7KBdn7Jn!5K%t>|9UqI%*%ig=4C`Y(r8E+R(i+#Y ivPwkEp7?-)DgX1nwW|x28uzm(Z1Y|5A?r{BgEaugWmO0O diff --git a/app/Plugin/comment/Lib/nbbc/smileys/frown.gif b/app/Plugin/comment/Lib/nbbc/smileys/frown.gif deleted file mode 100644 index fb126d49ab34ebb76b7d87825cc45840ca99653c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddQyHW=L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho-+lU2Z4qIjja59JPHmD49yG@Rwg?F7#Nv^CFAB;Tu5SMW|gVZX%JlGIzd8m(jAV# zr>;Hv!9gquhRcr57STSGbK>*DLmf8Fzy3%%EIP5slHV=mM9B)LMsHcGTCS}xLRN)N OP72+%Rr^{4gEat>bvV2L diff --git a/app/Plugin/comment/Lib/nbbc/smileys/irritated.gif b/app/Plugin/comment/Lib/nbbc/smileys/irritated.gif deleted file mode 100644 index 2ec3c5f65cb18a166c373aa6842c53759d214dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddQyHW=L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1q#9+bm^V8+*`4|KiJPc%Xu=QJ;CA#Pklf`x`n}h`oZmki#ECvU*c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(;1{WL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHc7#bM4g0v(U9F`pGR25{gIBDd-G*N(2M58k3AQPK*w46ae zLHbb^J8_+Wg5r#eGdx0#L_I^){hK4C!$da}tA#NzwmF4FBs>piaOTbkIk7eSAOjnZ eh=g|lL*u53)0ggAOl*i}vK0Pwbe4evgEau!4^8_3 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/lookleft.gif b/app/Plugin/comment/Lib/nbbc/smileys/lookleft.gif deleted file mode 100644 index c92fa5c0f3195da98d2585c2c5ffc8dfab5f2657..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddeGJkZA{+}A9Bkx}77-~}@Q{&_RhTKEV?ol< zHUV}&n+AnO_kIooCWRA*jZKr)gc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd6Bwj9L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!&2Ea0)s!PUmD|NLb*o+=H1le^$tb z1uL4SvNzm1GBfDu`33s6O@D3#tYlayryJzU6`0sKn~jlWMU=zhL#xesSHwmy&~{{G Z*>~#itFp%~Ock70d@{^)4mB`X0|3tyQGfsd diff --git a/app/Plugin/comment/Lib/nbbc/smileys/neutral.gif b/app/Plugin/comment/Lib/nbbc/smileys/neutral.gif deleted file mode 100644 index 19828c69c62d7944dfd8d92de48ac386c2744c9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddeGJkZA{+}A9Bkx}77-~}@Q{&_RhTKEf}zp1 zo1c+q&J6*_rha+B6qSqxNldI7focLP3zIxqSjBZTb{Hu)FzF{7EzrE=;5bK3^hC^p z1L6$@1!vclIW)4=qngW4_!|g2mS|tqv ziH>c;vJ3(S2@VbYj83ksB(SU5#igv?|(=+Vf)w;*K0f;3-NUC+EJGY=>>v+)(~ zD#>VA?$72JEvIm5^0Kq6PScq*x{RKBO;umYrNO0;%Cwq6K&HZS>g$H}%!lL*9D*KS oTf`{CX0hR$st+S8&x^a+2l7t#vNDNF+%#K!tewx8jfKG)0H$kLp#T5? diff --git a/app/Plugin/comment/Lib/nbbc/smileys/sleepy.gif b/app/Plugin/comment/Lib/nbbc/smileys/sleepy.gif deleted file mode 100644 index 7c0bc0295945b3c6d812f7329fa1b1a8422cffe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmZ?wbhEHb6krfw*vtR|XVQ%ST~hsbUH{)5%YP4@|3CBn|2q8ths6J1v;Q;v2P;7k zia%L685p=2bU;ENGZ;A97(_W_JOUINni;s1Y&;Ae9%yFd^5A)JVd23JR-O!#0EdH4 z{Sv$~5|s^$otPx~6(TwxEOuuQbXD@0z~JcF&A=vO!eGqk$-u%PA=7Zl!J9!l_Lc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(-@>VL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+RPX;+hHHHfxRxm7JV2hA(_}akO!NtwM8(`pYl}So8?r7$MwYQlP eIJp!QwynFdLt0(vhBrgftrjj`Hx~&325SIpHct)! diff --git a/app/Plugin/comment/Lib/nbbc/smileys/smile3.gif b/app/Plugin/comment/Lib/nbbc/smileys/smile3.gif deleted file mode 100644 index 43826af3ce2119ec250a73b03c2ad1531c0d4f32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddvl*m0L^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho+RPX;+hHHHfxRxr$AU{l}_U^vLIicLXFr@`3a+ImJl35^AT4@2j$ kJvybUFl_+?Q_AN_Yj+naHST9o*yg+9LV`;RBO`+~01rD-FNz{8*e;(*Lx;F!W7$sxkAV8Ovg4oMM_gar#38Civy1PT@`I@%?` z?q|ZFaDb78gO7#bg8;)pCSG?nj|&@>+*^70MM4~kpE5GDDlj={3O6*dvgL~@1WaJ? zn!uT%i9#Q4ZET2UlH}x) a*s$&0tsPS8d^fBe(hu#46lP;#um%9jQB2AJ diff --git a/app/Plugin/comment/Lib/nbbc/smileys/star.gif b/app/Plugin/comment/Lib/nbbc/smileys/star.gif deleted file mode 100644 index a764ae8d62094d00b987cd9685260db4b0cf2d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmZ?wbhEHb6krfw*vtR|wYAd!{xN+2%J}9L%fp8pH?HyQ-Yxx~;s5{twg3O`22x;E zP(tx13nv2u2ZIiX4>E&+qn1ICL#Bg);b1d6Ba1Skr*uplFFAs@R& wtX0BthgJ(#kExjx6&hwUiLp)aOjz#3#LZjA!(gU#W>VOBzmi#D2@VX_0DNRYt^fc4 diff --git a/app/Plugin/comment/Lib/nbbc/smileys/surprise.gif b/app/Plugin/comment/Lib/nbbc/smileys/surprise.gif deleted file mode 100644 index 541adf8ffbc25dfb39452fde6a15f7b373e4da08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmZ?wbhEHb6krfw*vtR|XVQ%ST~hsbUH{)5%YP4@|3CBn|2q8ths6J1v;Q;v2LiAv zD53b1g_D7Si$Mp(2bsaZ(Z?XlA;Pg>!NEojQ4yYi1&NIetintQ5eFJvyZIS;=0qHL z(8$bYETqDa(0H;*O`PGy3IhhusceDkS~wC8oStFCJzc8vy|L$1+d+7ZCneYGC;r~CSq@|tt z&+vcd%>QT3{09QXpDdgV3_J`vAe|sH7&vA!NOFj9ELh;!#LUSgP_W=3BO|LggT#&v zicQ_({0tce6CIB-a|m^aL|$-o>}S)_itsEt$lS>tEEco!zyoG>8AcHUh6hRwv$zBp z3K}*)@SDJyv5sZK0md_n6{l)>BwWbwTB0m$@nok!R#P`KgNX^l#pK4tj9hFI(F>BV k&t-dbN;hEI0tTk^&-2!L7b-UHXHh8gS+POuVFQCT0JC#gd;kCd diff --git a/app/Plugin/comment/Lib/nbbc/smileys/teeth.gif b/app/Plugin/comment/Lib/nbbc/smileys/teeth.gif deleted file mode 100644 index 86eff8a86cb715c75d257491b7a95845878eb839..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmZ?wbhEHb6krfw*vtR||1PQiyRQH5j^)3H&i|kJ{(l|*|3l*cui5_@{sRG634&1k z$->FNz{#Kk5(1gQz){N}%pt|&4@Ul=)}OxA!2f1;{w+v zW(E-hh6fIwlcXHeWGWdLo0!?O+I$!s8V=4jPrt^pz=84nJbOc?1)0Jb{_`D5#ay$l uJoi~-Q?GQ_a>Fv;m8`5c_GlSOjf||qObHbXjjrAN zj68F02sk$N%L}HcWGqNxV$}##6IfZ8Q0uw{$*s*dj7?f!rWMtVj aX>a6#x5qkJJ_$8Mm@RZ#!^zCVU=08s2v5-f diff --git a/app/Plugin/comment/Lib/nbbc/smileys/wink.gif b/app/Plugin/comment/Lib/nbbc/smileys/wink.gif deleted file mode 100644 index d8563fedb81ce08762e913a06397b61eb441764a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 229 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddlNh8qL^u{KIM~P`Eh18|;2|R;t1weS$AYAz zZ366mHVq1m?)@AFObRCq8=EGp3%8gU6e%|~aVO1-;S_9O>@$#*h>2W~&^TL8^hnGC zho_9w80x0ER2m)dns2UM_k}~@z{OTCT_p~N1q&N{Jf}*@JlLx4$jDkJ6>)l-!?DdQ amrnWq4twmvRKa=0C*%49j}}Ho25SH~JX4YY diff --git a/app/Plugin/comment/Lib/nbbc/smileys/wink3.gif b/app/Plugin/comment/Lib/nbbc/smileys/wink3.gif deleted file mode 100644 index 76b0d18b6c6a02e09dedbc2a16858ac43eb42d6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmZ?wbhEHb6krfw*vtR|Gt-REq!~Zh!0_*q>c8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQodd(;1{WL^u{KIM~P`Eh18|;2|R;t1weS$AYAz zZ366mHVq1m?)@AFObRCq8=EGp3%8gU6e%|~aVO1-;S_9O>@$#*h>2W~&^TL8^hnGC zho_9w80x0ER2m)dns2UMCG$Ygp#^ug|X;n9cjYsc0-X;J>AS fSLDQrX$`k`=e^9Dc8vy|L$1+d+7ZCneYGC;r~A*{{Nc& z|5xdMhW}u-42nNlI2jmt8FWBgkQoddlNqErL^u{KIM~P`Eh18|;2|R;t1weS1w*52 zH$NlKoErj;P5ttMDJmHYl9*UE0@Vao7AASJu!`$w>@ZSpVA4-ETA+E!!Euh7=!uvG z3znUqt?$1~=ho-v-u(*Mau!+)83!4d?U-h81tvB!Mf3A;1YBrf+`zK>k;&^t@MSJ`71rV37nD>Ei6IM~6#%*0>~0O8hC0RR91 diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php deleted file mode 100644 index 71345759..00000000 --- a/app/Plugin/comment/Lib/nbbc/src/nbbc_email.php +++ /dev/null @@ -1,158 +0,0 @@ -check_email_address('test@example.org')) { - // // Email address is technically valid - // } - // - // * Note: This version is slightly modified for PHP4 compatibility - // and to work well with NBBC. You can get the original at - // the URL shown above. - - class BBCodeEmailAddressValidator { - - // Check email address validity - // @param strEmailAddress Email address to be checked - // @return True if email is valid, false if not - function check_email_address($strEmailAddress) { - - // If magic quotes is "on", email addresses with quote marks will - // fail validation because of added escape characters. Uncommenting - // the next three lines will allow for this issue. - //if (get_magic_quotes_gpc()) { - // $strEmailAddress = stripslashes($strEmailAddress); - //} - - // Control characters are not allowed - if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $strEmailAddress)) { - return false; - } - - // Split it into sections using last instance of "@" - $intAtSymbol = strrpos($strEmailAddress, '@'); - if ($intAtSymbol === false) { - // No "@" symbol in email. - return false; - } - $arrEmailAddress[0] = substr($strEmailAddress, 0, $intAtSymbol); - $arrEmailAddress[1] = substr($strEmailAddress, $intAtSymbol + 1); - - // Count the "@" symbols. Only one is allowed, except where - // contained in quote marks in the local part. Quickest way to - // check this is to remove anything in quotes. - $arrTempAddress[0] = preg_replace('/"[^"]+"/' - ,'' - ,$arrEmailAddress[0]); - $arrTempAddress[1] = $arrEmailAddress[1]; - $strTempAddress = $arrTempAddress[0] . $arrTempAddress[1]; - // Then check - should be no "@" symbols. - if (strrpos($strTempAddress, '@') !== false) { - // "@" symbol found - return false; - } - - // Check local portion - if (!$this->check_local_portion($arrEmailAddress[0])) { - return false; - } - - // Check domain portion - if (!$this->check_domain_portion($arrEmailAddress[1])) { - return false; - } - - // If we're still here, all checks above passed. Email is valid. - return true; - - } - - // Checks email section before "@" symbol for validity - // @param strLocalPortion Text to be checked - // @return True if local portion is valid, false if not - function check_local_portion($strLocalPortion) { - // Local portion can only be from 1 to 64 characters, inclusive. - // Please note that servers are encouraged to accept longer local - // parts than 64 characters. - if (!$this->check_text_length($strLocalPortion, 1, 64)) { - return false; - } - // Local portion must be: - // 1) a dot-atom (strings separated by periods) - // 2) a quoted string - // 3) an obsolete format string (combination of the above) - $arrLocalPortion = explode('.', $strLocalPortion); - for ($i = 0, $max = sizeof($arrLocalPortion); $i < $max; $i++) { - if (!preg_match('.^(' - . '([A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]' - . '[A-Za-z0-9!#$%&\'*+/=?^_`{|}~-]{0,63})' - .'|' - . '("[^\\\"]{0,62}")' - .')$.' - ,$arrLocalPortion[$i])) { - return false; - } - } - return true; - } - - // Checks email section after "@" symbol for validity - // @param strDomainPortion Text to be checked - // @return True if domain portion is valid, false if not - function check_domain_portion($strDomainPortion) { - // Total domain can only be from 1 to 255 characters, inclusive - if (!$this->check_text_length($strDomainPortion, 1, 255)) { - return false; - } - // Check if domain is IP, possibly enclosed in square brackets. - if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' - .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}$/' - ,$strDomainPortion) || - preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' - .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])) {3}\]$/' - ,$strDomainPortion)) { - return true; - } else { - $arrDomainPortion = explode('.', $strDomainPortion); - if (sizeof($arrDomainPortion) < 2) { - return false; // Not enough parts to domain - } - for ($i = 0, $max = sizeof($arrDomainPortion); $i < $max; $i++) { - // Each portion must be between 1 and 63 characters, inclusive - if (!$this->check_text_length($arrDomainPortion[$i], 1, 63)) { - return false; - } - if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|' - .'([A-Za-z0-9]+))$/', $arrDomainPortion[$i])) { - return false; - } - } - } - return true; - } - - // Check given text length is between defined bounds - // @param strText Text to be checked - // @param intMinimum Minimum acceptable length - // @param intMaximum Maximum acceptable length - // @return True if string is within bounds (inclusive), false if not - function check_text_length($strText, $intMinimum, $intMaximum) { - // Minimum and maximum are both inclusive - $intTextLength = strlen($strText); - if (($intTextLength < $intMinimum) || ($intTextLength > $intMaximum)) { - return false; - } else { - return true; - } - } - - } - -?> \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php deleted file mode 100644 index 9d41beb5..00000000 --- a/app/Plugin/comment/Lib/nbbc/src/nbbc_lex.php +++ /dev/null @@ -1,591 +0,0 @@ -NextToken is called, the next token is returned until it returns - // BBCODE_EOI at the end of the input. - // - //----------------------------------------------------------------------------- - - class BBCodeLexer { - var $token; // Return token type: One of the BBCODE_* constants. - var $text; // Actual exact, original text of token. - var $tag; // If token is a tag, this is the decoded array version. - - var $state; // Next state of the lexer's state machine: text, or tag/ws/nl - var $input; // The input string, split into an array of tokens. - var $ptr; // Read pointer into the input array. - var $unget; // Whether to "unget" the last token. - - var $verbatim; // In verbatim mode, we return all input, unparsed, including comments. - var $debug; // In debug mode, we dump decoded tags when we find them. - - var $tagmarker; // Which kind of tag marker we're using: "[", "<", "(", or "{" - var $end_tagmarker; // The ending tag marker: "]", ">", "(", or "{" - var $pat_main; // Main tag-matching pattern. - var $pat_comment; // Pattern for matching comments. - var $pat_comment2; // Pattern for matching comments. - var $pat_wiki; // Pattern for matching wiki-links. - - function BBCodeLexer($string, $tagmarker = '[') { - // First thing we do is to split the input string into tuples of - // text and tags. This will make it easy to tokenize. We define a tag as - // anything starting with a [, ending with a ], and containing no [ or ] in - // between unless surrounded by "" or '', and containing no newlines. - // We also separate out whitespace and newlines. - - // Choose a tag marker based on the possible tag markers. - $regex_beginmarkers = Array( '[' => '\[', '<' => '<', '{' => '\{', '(' => '\(' ); - $regex_endmarkers = Array( '[' => '\]', '<' => '>', '{' => '\}', '(' => '\)' ); - $endmarkers = Array( '[' => ']', '<' => '>', '{' => '}', '(' => ')' ); - if (!isset($regex_endmarkers[$tagmarker])) $tagmarker = '['; - $e = $regex_endmarkers[$tagmarker]; - $b = $regex_beginmarkers[$tagmarker]; - $this->tagmarker = $tagmarker; - $this->end_tagmarker = $endmarkers[$tagmarker]; - - // $this->input will be an array of tokens, with the special property that - // the elements strictly alternate between plain text and tags/whitespace/newlines, - // and that tags always have *two* entries per tag. The first element will - // always be plain text. Note that the regexes below make VERY heavy use of - // PCRE regex-syntax extensions, so don't even try to modify them unless you - // know how things like (?!) and (?:) and (?=) work. We use the /x modifier - // here to make this a *lot* more legible and debuggable. - $this->pat_main = "/( " - // Match tags, as long as they do not start with [-- or [' or [!-- or [rem or [[. - // Tags may contain "quoted" or 'quoted' sections that may contain [ or ] characters. - // Tags may not contain newlines. - . "{$b}" - . "(?! -- | ' | !-- | {$b}{$b} )" - . "(?: [^\\n\\r{$b}{$e}] | \\\" [^\\\"\\n\\r]* \\\" | \\' [^\\'\\n\\r]* \\' )*" - . "{$e}" - - // Match wiki-links, which are of the form [[...]] or [[...|...]]. Unlike - // tags, wiki-links treat " and ' marks as normal input characters; but they - // still may not contain newlines. - . "| {$b}{$b} (?: [^{$e}\\r\\n] | {$e}[^{$e}\\r\\n] )* {$e}{$e}" - - // Match single-line comments, which start with [-- or [' or [rem . - . "| {$b} (?: -- | ' ) (?: [^{$e}\\n\\r]*) {$e}" - - // Match multi-line comments, which start with [!-- and end with --] and contain - // no --] in between. - . "| {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e}" - - // Match five or more hyphens as a special token, which gets returned as a [rule] tag. - . "| -----+" - - // Match newlines, in all four possible forms. - . "| \\x0D\\x0A | \\x0A\\x0D | \\x0D | \\x0A" - - // Match whitespace, but only if it butts up against a newline, rule, or - // bracket on at least one end. - . "| [\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+(?=[\\x0D\\x0A{$b}]|-----|$)" - . "| (?<=[\\x0D\\x0A{$e}]|-----|^)[\\x00-\\x09\\x0B-\\x0C\\x0E-\\x20]+" - - . " )/Dx"; - - $this->input = preg_split($this->pat_main, $string, -1, PREG_SPLIT_DELIM_CAPTURE); - - // Patterns for matching specific types of tokens during lexing. - $this->pat_comment = "/^ {$b} (?: -- | ' ) /Dx"; - $this->pat_comment2 = "/^ {$b}!-- (?: [^-] | -[^-] | --[^{$e}] )* --{$e} $/Dx"; - $this->pat_wiki = "/^ {$b}{$b} ([^\\|]*) (?:\\|(.*))? {$e}{$e} $/Dx"; - - // Current lexing state. - $this->ptr = 0; - $this->unget = false; - $this->state = BBCODE_LEXSTATE_TEXT; - $this->verbatim = false; - - // Return values. - $this->token = BBCODE_EOI; - $this->tag = false; - $this->text = ""; - } - - // Compute how many non-tag characters there are in the input, give or take a few. - // This is optimized for speed, not accuracy, so it'll get some stuff like - // horizontal rules and weird whitespace characters wrong, but it's only supposed - // to provide a rough quick guess, not a hard fact. - function GuessTextLength() { - $length = 0; - $ptr = 0; - $state = BBCODE_LEXSTATE_TEXT; - - // Loop until we find a valid (nonempty) token. - while ($ptr < count($this->input)) { - $text = $this->input[$ptr++]; - - if ($state == BBCODE_LEXSTATE_TEXT) { - $state = BBCODE_LEXSTATE_TAG; - $length += strlen($text); - } - else { - switch (ord(substr($this->text, 0, 1))) { - case 10: - case 13: - $state = BBCODE_LEXSTATE_TEXT; - $length++; - break; - default: - $state = BBCODE_LEXSTATE_TEXT; - $length += strlen($text); - break; - case 40: - case 60: - case 91: - case 123: - $state = BBCODE_LEXSTATE_TEXT; - break; - } - } - } - - return $length; - } - - // Return the type of the next token, either BBCODE_TAG or BBCODE_TEXT or - // BBCODE_EOI. This stores the content of this token into $this->text, the - // type of this token in $this->token, and possibly an array into $this->tag. - // - // If this is a BBCODE_TAG token, $this->tag will be an array computed from - // the tag's contents, like this: - // Array( - // '_name' => tag_name, - // '_end' => true if this is an end tag (i.e., the name starts with a /) - // '_default' => default value (for example, in [url=foo], this is "foo"). - // ... - // ...all other key => value parameters given in the tag... - // ... - // ) - function NextToken() { - - // Handle ungets; if the last token has been "ungotten", just return it again. - if ($this->unget) { - $this->unget = false; - return $this->token; - } - - // Loop until we find a valid (nonempty) token. - while (true) { - - // Did we run out of tokens in the input? - if ($this->ptr >= count($this->input)) { - $this->text = ""; - $this->tag = false; - return $this->token = BBCODE_EOI; - } - - // Inhale one token, sanitizing away any weird control characters. We - // allow \t, \r, and \n to pass through, but that's it. - $this->text = preg_replace("/[\\x00-\\x08\\x0B-\\x0C\\x0E-\\x1F]/", "", - $this->input[$this->ptr++]); - - if ($this->verbatim) { - - // In verbatim mode, we return *everything* as plain text or whitespace. - $this->tag = false; - if ($this->state == BBCODE_LEXSTATE_TEXT) { - $this->state = BBCODE_LEXSTATE_TAG; - $token_type = BBCODE_TEXT; - } - else { - // This must be either whitespace, a newline, or a tag. - $this->state = BBCODE_LEXSTATE_TEXT; - switch (ord(substr($this->text, 0, 1))) { - case 10: - case 13: - // Newline. - $token_type = BBCODE_NL; - break; - default: - // Whitespace. - $token_type = BBCODE_WS; - break; - case 45: - case 40: - case 60: - case 91: - case 123: - // Tag or comment. - $token_type = BBCODE_TEXT; - break; - } - } - - if (strlen($this->text) > 0) - return $this->token = $token_type; - } - else if ($this->state == BBCODE_LEXSTATE_TEXT) { - // Next up is plain text, but only return it if it's nonempty. - $this->state = BBCODE_LEXSTATE_TAG; - $this->tag = false; - if (strlen($this->text) > 0) - return $this->token = BBCODE_TEXT; - } - else { - // This must be either whitespace, a newline, or a tag. - switch (ord(substr($this->text, 0, 1))) { - case 10: - case 13: - // Newline. - $this->tag = false; - $this->state = BBCODE_LEXSTATE_TEXT; - return $this->token = BBCODE_NL; - case 45: - // A rule made of hyphens; return it as a [rule] tag. - if (preg_match("/^-----/", $this->text)) { - $this->tag = Array('_name' => 'rule', '_endtag' => false, '_default' => ''); - $this->state = BBCODE_LEXSTATE_TEXT; - return $this->token = BBCODE_TAG; - } - else { - $this->tag = false; - $this->state = BBCODE_LEXSTATE_TEXT; - if (strlen($this->text) > 0) - return $this->token = BBCODE_TEXT; - continue; - } - default: - // Whitespace. - $this->tag = false; - $this->state = BBCODE_LEXSTATE_TEXT; - return $this->token = BBCODE_WS; - case 40: - case 60: - case 91: - case 123: - // Tag or comment. This is the most complicated one, because it - // needs to be parsed into its component pieces. - - // See if this is a comment; if so, skip it. - if (preg_match($this->pat_comment, $this->text)) { - // This is a comment, not a tag, so treat it like it doesn't exist. - $this->state = BBCODE_LEXSTATE_TEXT; - continue; - } - if (preg_match($this->pat_comment2, $this->text)) { - // This is a comment, not a tag, so treat it like it doesn't exist. - $this->state = BBCODE_LEXSTATE_TEXT; - continue; - } - - // See if this is a [[wiki link]]; if so, convert it into a [wiki="" title=""] tag. - if (preg_match($this->pat_wiki, $this->text, $matches)) { - $this->tag = Array('_name' => 'wiki', '_endtag' => false, - '_default' => @$matches[1], 'title' => @$matches[2]); - $this->state = BBCODE_LEXSTATE_TEXT; - return $this->token = BBCODE_TAG; - } - - // Not a comment, so parse it like a tag. - $this->tag = $this->Internal_DecodeTag($this->text); - $this->state = BBCODE_LEXSTATE_TEXT; - return $this->token = ($this->tag['_end'] ? BBCODE_ENDTAG : BBCODE_TAG); - } - } - } - } - - // Ungets the last token read so that a subsequent call to NextToken() will - // return it. Note that UngetToken() does not switch states when you switch - // between verbatim mode and standard mode: For example, if you read a tag, - // unget the tag, switch to verbatim mode, and then get the next token, you'll - // get back a BBCODE_TAG --- exactly what you ungot, not a BBCODE_TEXT token. - function UngetToken() { - if ($this->token !== BBCODE_EOI) - $this->unget = true; - } - - // Peek at the next token, but don't remove it. - function PeekToken() { - $result = $this->NextToken(); - if ($this->token !== BBCODE_EOI) - $this->unget = true; - return $result; - } - - // Save the state of this lexer so it can be restored later. The return - // value from this should be considered opaque. Because PHP uses copy-on-write - // references, the total cost of the returned state is relatively small, and - // the running time of this function (and RestoreState) is very fast. - function SaveState() { - return Array( - 'token' => $this->token, - 'text' => $this->text, - 'tag' => $this->tag, - 'state' => $this->state, - 'input' => $this->input, - 'ptr' => $this->ptr, - 'unget' => $this->unget, - 'verbatim' => $this->verbatim - ); - } - - // Restore the state of this lexer from a saved previous state. - function RestoreState($state) { - if (!is_array($state)) return; - $this->token = @$state['token']; - $this->text = @$state['text']; - $this->tag = @$state['tag']; - $this->state = @$state['state']; - $this->input = @$state['input']; - $this->ptr = @$state['ptr']; - $this->unget = @$state['unget']; - $this->verbatim = @$state['verbatim']; - } - - // Given a string, if it's surrounded by "quotes" or 'quotes', remove them. - function Internal_StripQuotes($string) { - if (preg_match("/^\\\"(.*)\\\"$/", $string, $matches)) - return $matches[1]; - else if (preg_match("/^\\'(.*)\\'$/", $string, $matches)) - return $matches[1]; - else return $string; - } - - // Given a tokenized piece of a tag, decide what type of token it is. Our - // return values are: - // -1 End-of-input (EOI). - // '=' Token is an = sign. - // ' ' Token is whitespace. - // '"' Token is quoted text. - // 'A' Token is unquoted text. - function Internal_ClassifyPiece($ptr, $pieces) { - if ($ptr >= count($pieces)) return -1; // EOI. - $piece = $pieces[$ptr]; - if ($piece == '=') return '='; - else if (preg_match("/^[\\'\\\"]/", $piece)) return '"'; - else if (preg_match("/^[\\x00-\\x20]+$/", $piece)) return ' '; - else return 'A'; - } - - // Given a string containing a complete [tag] (including its brackets), break - // it down into its components and return them as an array. - function Internal_DecodeTag($tag) { - - if ($this->debug) { - print "Lexer::InternalDecodeTag: input: " . htmlspecialchars($tag) . "
    \n"; - } - - // Create the initial result object. - $result = Array('_tag' => $tag, '_endtag' => '', '_name' => '', - '_hasend' => false, '_end' => false, '_default' => false); - - // Strip off the [brackets] around the tag, leaving just its content. - $tag = substr($tag, 1, strlen($tag)-2); - - // The starting bracket *must* be followed by a non-whitespace character. - $ch = ord(substr($tag, 0, 1)); - if ($ch >= 0 && $ch <= 32) return $result; - - // Break it apart into words, quoted text, whitespace, and equal signs. - $pieces = preg_split("/(\\\"[^\\\"]+\\\"|\\'[^\\']+\\'|=|[\\x00-\\x20]+)/", - $tag, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); - $ptr = 0; - - // Handle malformed (empty) tags correctly. - if (count($pieces) < 1) return $result; - - // The first piece should be the tag name, whatever it is. If it starts with a / - // we remove the / and mark it as an end tag. - if (@substr($pieces[$ptr], 0, 1) == '/') { - $result['_name'] = strtolower(substr($pieces[$ptr++], 1)); - $result['_end'] = true; - } - else { - $result['_name'] = strtolower($pieces[$ptr++]); - $result['_end'] = false; - } - - // Skip whitespace after the tag name. - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') - $ptr++; - - $params = Array(); - - // If the next piece is an equal sign, then the tag's default value follows. - if ($type != '=') { - $result['_default'] = false; - $params[] = Array('key' => '', 'value' => ''); - } - else { - $ptr++; - - // Skip whitespace after the initial equal-sign. - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') - $ptr++; - - // Examine the next (real) piece, and see if it's quoted; if not, we need to - // use heuristics to guess where the default value begins and ends. - if ($type == "\"") - $value = $this->Internal_StripQuotes($pieces[$ptr++]); - else { - // Collect pieces going forward until we reach an = sign or the end of the - // tag; then rewind before whatever comes before the = sign, and everything - // between here and there becomes the default value. This allows tags like - // [font=Times New Roman size=4] to make sense even though the font name is - // not quoted. Note, however, that there's a special initial case, where - // any equal-signs before whitespace are considered to be part of the parameter - // as well; this allows an ugly tag like [url=http://foo?bar=baz target=my_window] - // to behave in a way that makes (tolerable) sense. - $after_space = false; - $start = $ptr; - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { - if ($type == ' ') $after_space = true; - if ($type == '=' && $after_space) break; - $ptr++; - } - if ($type == -1) $ptr--; - - // We've now found the first (appropriate) equal-sign after the start of the - // default value. (In the example above, that's the "=" after "target".) We - // now have to rewind back to the last whitespace to find where the default - // value ended. - if ($type == '=') { - // Rewind before = sign. - $ptr--; - // Rewind before any whitespace before = sign. - while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) == ' ') - $ptr--; - // Rewind before any text elements before that. - while ($ptr > $start && $this->Internal_ClassifyPiece($ptr, $pieces) != ' ') - $ptr--; - } - - // The default value is everything from $start to $ptr, inclusive. - $value = ""; - for (; $start <= $ptr; $start++) { - if ($this->Internal_ClassifyPiece($start, $pieces) == ' ') - $value .= " "; - else $value .= $this->Internal_StripQuotes($pieces[$start]); - } - $value = trim($value); - - $ptr++; - } - - $result['_default'] = $value; - $params[] = Array('key' => '', 'value' => $value); - } - - // The rest of the tag is composed of either floating keys or key=value pairs, so walk through - // the tag and collect them all. Again, we have the nasty special case where an equal sign - // in a parameter but before whitespace counts as part of that parameter. - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1) { - - // Skip whitespace before the next key name. - while ($type == ' ') { - $ptr++; - $type = $this->Internal_ClassifyPiece($ptr, $pieces); - } - - // Decode the key name. - if ($type == 'A' || $type == '"') - $key = strtolower($this->Internal_StripQuotes(@$pieces[$ptr++])); - else if ($type == '=') { - $ptr++; - continue; - } - else if ($type == -1) break; - - // Skip whitespace after the key name. - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') - $ptr++; - - // If an equal-sign follows, we need to collect a value. Otherwise, we - // take the key itself as the value. - if ($type != '=') - $value = $this->Internal_StripQuotes($key); - else { - $ptr++; - // Skip whitespace after the equal sign. - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) == ' ') - $ptr++; - if ($type == '"') { - // If we get a quoted value, take that as the only value. - $value = $this->Internal_StripQuotes($pieces[$ptr++]); - } - else if ($type != -1) { - // If we get a non-quoted value, consume non-quoted values - // until we reach whitespace. - $value = $pieces[$ptr++]; - while (($type = $this->Internal_ClassifyPiece($ptr, $pieces)) != -1 - && $type != ' ') - $value .= $pieces[$ptr++]; - } - else $value = ""; - } - - // Record this in the associative array if it's a legal public identifier name. - // Legal *public* identifier names must *not* begin with an underscore. - if (substr($key, 0, 1) != '_') - $result[$key] = $value; - - // Record this in the parameter list always. - $params[] = Array('key' => $key, 'value' => $value); - } - - // Add the parameter list as a member of the associative array. - $result['_params'] = $params; - - if ($this->debug) { - // In debugging modes, output the tag as we collected it. - print "Lexer::InternalDecodeTag: output: "; - ob_start(); - print_r($result); - $output = ob_get_clean(); - print htmlspecialchars($output) . "
    \n"; - } - - // Save the resulting parameters, and return the whole shebang. - return $result; - } - } - -?> \ No newline at end of file diff --git a/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php b/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php deleted file mode 100644 index af0bcd34..00000000 --- a/app/Plugin/comment/Lib/nbbc/src/nbbc_lib.php +++ /dev/null @@ -1,655 +0,0 @@ -AddRule(...); - // $bbcode->AddSmiley(...); - // - //----------------------------------------------------------------------------- - - class BBCodeLibrary { - - //----------------------------------------------------------------------------- - // Standard library of smiley definitions. - - var $default_smileys = Array( - ':)' => 'smile.gif', ':-)' => 'smile.gif', - '=)' => 'smile.gif', '=-)' => 'smile.gif', - ':(' => 'frown.gif', ':-(' => 'frown.gif', - '=(' => 'frown.gif', '=-(' => 'frown.gif', - ':D' => 'bigsmile.gif', ':-D' => 'bigsmile.gif', - '=D' => 'bigsmile.gif', '=-D' => 'bigsmile.gif', - '>:('=> 'angry.gif', '>:-('=> 'angry.gif', - '>=('=> 'angry.gif', '>=-('=> 'angry.gif', - 'D:' => 'angry.gif', 'D-:' => 'angry.gif', - 'D=' => 'angry.gif', 'D-=' => 'angry.gif', - '>:)'=> 'evil.gif', '>:-)'=> 'evil.gif', - '>=)'=> 'evil.gif', '>=-)'=> 'evil.gif', - '>:D'=> 'evil.gif', '>:-D'=> 'evil.gif', - '>=D'=> 'evil.gif', '>=-D'=> 'evil.gif', - '>;)'=> 'sneaky.gif', '>;-)'=> 'sneaky.gif', - '>;D'=> 'sneaky.gif', '>;-D'=> 'sneaky.gif', - 'O:)' => 'saint.gif', 'O:-)' => 'saint.gif', - 'O=)' => 'saint.gif', 'O=-)' => 'saint.gif', - ':O' => 'surprise.gif', ':-O' => 'surprise.gif', - '=O' => 'surprise.gif', '=-O' => 'surprise.gif', - ':?' => 'confuse.gif', ':-?' => 'confuse.gif', - '=?' => 'confuse.gif', '=-?' => 'confuse.gif', - ':s' => 'worry.gif', ':-S' => 'worry.gif', - '=s' => 'worry.gif', '=-S' => 'worry.gif', - ':|' => 'neutral.gif', ':-|' => 'neutral.gif', - '=|' => 'neutral.gif', '=-|' => 'neutral.gif', - ':I' => 'neutral.gif', ':-I' => 'neutral.gif', - '=I' => 'neutral.gif', '=-I' => 'neutral.gif', - ':/' => 'irritated.gif', ':-/' => 'irritated.gif', - '=/' => 'irritated.gif', '=-/' => 'irritated.gif', - ':\\' => 'irritated.gif', ':-\\' => 'irritated.gif', - '=\\' => 'irritated.gif', '=-\\' => 'irritated.gif', - ':P' => 'tongue.gif', ':-P' => 'tongue.gif', - '=P' => 'tongue.gif', '=-P' => 'tongue.gif', - 'X-P' => 'tongue.gif', - '8)' => 'bigeyes.gif', '8-)' => 'bigeyes.gif', - 'B)' => 'cool.gif', 'B-)' => 'cool.gif', - ';)' => 'wink.gif', ';-)' => 'wink.gif', - ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', - '^_^'=> 'anime.gif', '^^;' => 'sweatdrop.gif', - '>_>'=> 'lookright.gif', '>.>' => 'lookright.gif', - '<_<'=> 'lookleft.gif', '<.<' => 'lookleft.gif', - 'XD' => 'laugh.gif', 'X-D' => 'laugh.gif', - ';D' => 'bigwink.gif', ';-D' => 'bigwink.gif', - ':3' => 'smile3.gif', ':-3' => 'smile3.gif', - '=3' => 'smile3.gif', '=-3' => 'smile3.gif', - ';3' => 'wink3.gif', ';-3' => 'wink3.gif', - '' => 'teeth.gif', '' => 'teeth.gif', - 'o.O' => 'boggle.gif', 'O.o' => 'boggle.gif', - ':blue:' => 'blue.gif', - ':zzz:' => 'sleepy.gif', - '<3' => 'heart.gif', - ':star:' => 'star.gif', - ); - - //----------------------------------------------------------------------------- - // Standard rules for what to do when a BBCode tag is encountered. - - var $default_tag_rules = Array( - - 'b' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'plain_start' => "", - 'plain_end' => "", - ), - 'i' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'plain_start' => "", - 'plain_end' => "", - ), - 'u' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'plain_start' => "", - 'plain_end' => "", - ), - 's' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'plain_start' => "", - 'plain_end' => "", - ), - - 'font' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'allow' => Array('_default' => '/^[a-zA-Z0-9._ -]+$/'), - 'method' => 'DoFont', - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'color' => Array( - 'mode' => BBCODE_MODE_ENHANCED, - 'allow' => Array('_default' => '/^#?[a-zA-Z0-9._ -]+$/'), - 'template' => '{$_content/v}', - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'size' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'allow' => Array('_default' => '/^[0-9.]+$/D'), - 'method' => 'DoSize', - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'sup' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'sub' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'spoiler' => Array( - 'simple_start' => "", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - 'acronym' => Array( - 'mode' => BBCODE_MODE_ENHANCED, - 'template' => '{$_content/v}', - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - ), - - 'url' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => 'DoURL', - 'class' => 'link', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), - 'content' => BBCODE_REQUIRED, - 'plain_start' => "", - 'plain_end' => "", - 'plain_content' => Array('_content', '_default'), - 'plain_link' => Array('_default', '_content'), - ), - 'email' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => 'DoEmail', - 'class' => 'link', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), - 'content' => BBCODE_REQUIRED, - 'plain_start' => "", - 'plain_end' => "", - 'plain_content' => Array('_content', '_default'), - 'plain_link' => Array('_default', '_content'), - ), - 'wiki' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => "DoWiki", - 'class' => 'link', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline'), - 'end_tag' => BBCODE_PROHIBIT, - 'content' => BBCODE_PROHIBIT, - 'plain_start' => "[", - 'plain_end' => "]", - 'plain_content' => Array('title', '_default'), - 'plain_link' => Array('_default', '_content'), - ), - - 'img' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => "DoImage", - 'class' => 'image', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'end_tag' => BBCODE_REQUIRED, - 'content' => BBCODE_REQUIRED, - 'plain_start' => "[image]", - 'plain_content' => Array(), - ), - 'rule' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => "DoRule", - 'class' => 'block', - 'allow_in' => Array('listitem', 'block', 'columns'), - 'end_tag' => BBCODE_PROHIBIT, - 'content' => BBCODE_PROHIBIT, - 'before_tag' => "sns", - 'after_tag' => "sns", - 'plain_start' => "\n-----\n", - 'plain_end' => "", - 'plain_content' => Array(), - ), - 'br' => Array( - 'mode' => BBCODE_MODE_SIMPLE, - 'simple_start' => "
    \n", - 'simple_end' => "", - 'class' => 'inline', - 'allow_in' => Array('listitem', 'block', 'columns', 'inline', 'link'), - 'end_tag' => BBCODE_PROHIBIT, - 'content' => BBCODE_PROHIBIT, - 'before_tag' => "s", - 'after_tag' => "s", - 'plain_start' => "\n", - 'plain_end' => "", - 'plain_content' => Array(), - ), - - 'left' => Array( - 'simple_start' => "\n
    \n", - 'simple_end' => "\n
    \n", - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - 'right' => Array( - 'simple_start' => "\n
    \n", - 'simple_end' => "\n
    \n", - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - 'center' => Array( - 'simple_start' => "\n
    \n", - 'simple_end' => "\n
    \n", - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - 'indent' => Array( - 'simple_start' => "\n
    \n", - 'simple_end' => "\n
    \n", - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - - 'columns' => Array( - 'simple_start' => "\n
    \n", - 'simple_end' => "\n
    \n", - 'class' => 'columns', - 'allow_in' => Array('listitem', 'block', 'columns'), - 'end_tag' => BBCODE_REQUIRED, - 'content' => BBCODE_REQUIRED, - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - 'nextcol' => Array( - 'simple_start' => "\n\n", - 'class' => 'nextcol', - 'allow_in' => Array('columns'), - 'end_tag' => BBCODE_PROHIBIT, - 'content' => BBCODE_PROHIBIT, - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "", - ), - - 'code' => Array( - 'mode' => BBCODE_MODE_ENHANCED, - 'template' => "\n
    \n
    Code:
    \n
    {\$_content/v}
    \n
    \n", - 'class' => 'code', - 'allow_in' => Array('listitem', 'block', 'columns'), - 'content' => BBCODE_VERBATIM, - 'before_tag' => "sns", - 'after_tag' => "sn", - 'before_endtag' => "sn", - 'after_endtag' => "sns", - 'plain_start' => "\nCode:\n", - 'plain_end' => "\n", - ), - 'quote' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => "DoQuote", - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\nQuote:\n", - 'plain_end' => "\n", - ), - - 'list' => Array( - 'mode' => BBCODE_MODE_LIBRARY, - 'method' => 'DoList', - 'class' => 'list', - 'allow_in' => Array('listitem', 'block', 'columns'), - 'before_tag' => "sns", - 'after_tag' => "sns", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n", - 'plain_end' => "\n", - ), - '*' => Array( - 'simple_start' => "
  • ", - 'simple_end' => "
  • \n", - 'class' => 'listitem', - 'allow_in' => Array('list'), - 'end_tag' => BBCODE_OPTIONAL, - 'before_tag' => "s", - 'after_tag' => "s", - 'before_endtag' => "sns", - 'after_endtag' => "sns", - 'plain_start' => "\n * ", - 'plain_end' => "\n", - ), - ); - - //----------------------------------------------------------------------------- - // Standard library of BBCode formatting routines. - - // Format a [url] tag by producing an ... element. - // The URL only allows http, https, mailto, and ftp protocols for safety. - function DoURL($bbcode, $action, $name, $default, $params, $content) { - // We can't check this with BBCODE_CHECK because we may have no URL before the content - // has been processed. - if ($action == BBCODE_CHECK) return true; - - $url = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); - if ($bbcode->IsValidURL($url)) { - if ($bbcode->debug) - print "ISVALIDURL
    "; - if ($bbcode->url_targetable !== false && isset($params['target'])) - $target = " target=\"" . htmlspecialchars($params['target']) . "\""; - else $target = ""; - if ($bbcode->url_target !== false) - if (!($bbcode->url_targetable == 'override' && isset($params['target']))) - $target = " target=\"" . htmlspecialchars($bbcode->url_target) . "\""; - return '' . $content . ''; - } - else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); - } - - // Format an [email] tag by producing an ... element. - // The e-mail address must be a valid address including at least a '@' and a valid domain - // name or IPv4 or IPv6 address after the '@'. - function DoEmail($bbcode, $action, $name, $default, $params, $content) { - // We can't check this with BBCODE_CHECK because we may have no URL before the content - // has been processed. - if ($action == BBCODE_CHECK) return true; - - $email = is_string($default) ? $default : $bbcode->UnHTMLEncode(strip_tags($content)); - if ($bbcode->IsValidEmail($email)) - return '' . $content . ''; - else return htmlspecialchars($params['_tag']) . $content . htmlspecialchars($params['_endtag']); - } - - // Format a [size] tag by producing a with a style with a different font-size. - function DoSize($bbcode, $action, $name, $default, $params, $content) { - switch ($default) { - case '0': $size = '.5em'; break; - case '1': $size = '.67em'; break; - case '2': $size = '.83em'; break; - default: - case '3': $size = '1.0em'; break; - case '4': $size = '1.17em'; break; - case '5': $size = '1.5em'; break; - case '6': $size = '2.0em'; break; - case '7': $size = '2.5em'; break; - } - return "$content"; - } - - // Format a [font] tag by producing a with a style with a different font-family. - // This is complicated by the fact that we have to recognize the five special font - // names and quote all the others. - function DoFont($bbcode, $action, $name, $default, $params, $content) { - $fonts = explode(",", $default); - $result = ""; - $special_fonts = Array( - 'serif' => 'serif', - 'sans-serif' => 'sans-serif', - 'sans serif' => 'sans-serif', - 'sansserif' => 'sans-serif', - 'sans' => 'sans-serif', - 'cursive' => 'cursive', - 'fantasy' => 'fantasy', - 'monospace' => 'monospace', - 'mono' => 'monospace', - ); - foreach ($fonts as $font) { - $font = trim($font); - if (isset($special_fonts[$font])) { - if (strlen($result) > 0) $result .= ","; - $result .= $special_fonts[$font]; - } - else if (strlen($font) > 0) { - if (strlen($result) > 0) $result .= ","; - $result .= "'$font'"; - } - } - return "$content"; - } - - // Format a [wiki] tag by producing an ... element. - function DoWiki($bbcode, $action, $name, $default, $params, $content) { - $name = $bbcode->Wikify($default); - if ($action == BBCODE_CHECK) - return strlen($name) > 0; - $title = trim(@$params['title']); - if (strlen($title) <= 0) $title = trim($default); - return "wiki_url}$name\" class=\"bbcode_wiki\">" - . htmlspecialchars($title) . ""; - } - - // Format an [img] tag. The URL only allows http, https, and ftp protocols for safety. - function DoImage($bbcode, $action, $name, $default, $params, $content) { - // We can't validate this until we have its content. - if ($action == BBCODE_CHECK) return true; - - $content = trim($bbcode->UnHTMLEncode(strip_tags($content))); - if (preg_match("/\\.(?:gif|jpeg|jpg|jpe|png)$/", $content)) { - if (preg_match("/^[a-zA-Z0-9_][^:]+$/", $content)) { - // No protocol, so the image is in our local image directory, or somewhere under it. - if (!preg_match("/(?:\\/\\.\\.\\/)|(?:^\\.\\.\\/)|(?:^\\/)/", $content)) { - $info = @getimagesize("{$bbcode->local_img_dir}/{$content}"); - if ($info[2] == IMAGETYPE_GIF || $info[2] == IMAGETYPE_JPEG || $info[2] == IMAGETYPE_PNG) { - return "local_img_url}/{$content}") . "\" alt=\"" - . htmlspecialchars(basename($content)) . "\" width=\"" - . htmlspecialchars($info[0]) . "\" height=\"" - . htmlspecialchars($info[1]) . "\" class=\"bbcode_img\" />"; - } - } - } - else if ($bbcode->IsValidURL($content, false)) { - // Remote URL, or at least we don't know where it is. - return "\"""; - } - } - - return htmlspecialchars($params['_tag']) . htmlspecialchars($content) . htmlspecialchars($params['_endtag']); - } - - // Format a [rule] tag. This substitutes the content provided by the BBCode - // object, whatever that may be. - function DoRule($bbcode, $action, $name, $default, $params, $content) { - if ($action == BBCODE_CHECK) return true; - else return $bbcode->rule_html; - } - - // Format a [quote] tag. This tag can come in a variety of flavors: - // - // [quote]...[/quote] - // [quote=Tom]...[/quote] - // [quote name="Tom"]...[/quote] - // - // In the third form, you can also add a date="" parameter to display the date - // on which Tom wrote it, and you can add a url="" parameter to turn the author's - // name into a link. A full example might be: - // - // [quote name="Tom" date="July 4, 1776 3:48 PM" url="http://www.constitution.gov"]...[/quote] - // - // The URL only allows http, https, mailto, gopher, ftp, and feed protocols for safety. - function DoQuote($bbcode, $action, $name, $default, $params, $content) { - if ($action == BBCODE_CHECK) return true; - - if (isset($params['name'])) { - $title = htmlspecialchars(trim($params['name'])) . " wrote"; - if (isset($params['date'])) - $title .= " on " . htmlspecialchars(trim($params['date'])); - $title .= ":"; - if (isset($params['url'])) { - $url = trim($params['url']); - if ($bbcode->IsValidURL($url)) - $title = "" . $title . ""; - } - } - else if (!is_string($default)) - $title = "Quote:"; - else $title = htmlspecialchars(trim($default)) . " wrote:"; - return "\n
    \n
    " - . $title . "
    \n
    " - . $content . "
    \n
    \n"; - } - - // Format a [list] tag, which is complicated by the number of different - // ways a list can be started. The following parameters are allowed: - // - // [list] Unordered list, using default marker - // [list=circle] Unordered list, using circle marker - // [list=disc] Unordered list, using disc marker - // [list=square] Unordered list, using square marker - // - // [list=1] Ordered list, numeric, starting at 1 - // [list=A] Ordered list, capital letters, starting at A - // [list=a] Ordered list, lowercase letters, starting at a - // [list=I] Ordered list, capital Roman numerals, starting at I - // [list=i] Ordered list, lowercase Roman numerals, starting at i - // [list=greek] Ordered list, lowercase Greek letters, starting at alpha - // [list=01] Ordered list, two-digit numeric with 0-padding, starting at 01 - function DoList($bbcode, $action, $name, $default, $params, $content) { - - // Allowed list styles, striaght from the CSS 2.1 spec. The only prohibited - // list style is that with image-based markers, which often slows down web sites. - $list_styles = Array( - '1' => 'decimal', - '01' => 'decimal-leading-zero', - 'i' => 'lower-roman', - 'I' => 'upper-roman', - 'a' => 'lower-alpha', - 'A' => 'upper-alpha', - ); - $ci_list_styles = Array( - 'circle' => 'circle', - 'disc' => 'disc', - 'square' => 'square', - 'greek' => 'lower-greek', - 'armenian' => 'armenian', - 'georgian' => 'georgian', - ); - $ul_types = Array( - 'circle' => 'circle', - 'disc' => 'disc', - 'square' => 'square', - ); - - $default = trim($default); - - if ($action == BBCODE_CHECK) { - if (!is_string($default) || strlen($default) == "") return true; - else if (isset($list_styles[$default])) return true; - else if (isset($ci_list_styles[strtolower($default)])) return true; - else return false; - } - - // Choose a list element (