From 9e1be4fddff9028958d6d0c8ad93e5c08a8b7136 Mon Sep 17 00:00:00 2001 From: maraby Date: Wed, 19 Dec 2007 00:40:11 -0500 Subject: [PATCH] Added Canvas 1.1.0, originally not under SCM so no historical development records exist. All development by mtodd (aka maraby). --- config/.htaccess | 5 + config/config.cache | 0 config/config.yml | 19 + config/database.yml | 20 + config/routes.php | 33 + controllers/application_controller.php | 122 ++ dispatcher.php | 58 + extensions/Auth.php | 67 + extensions/Auth2.php | 46 + extensions/LDAP.php | 274 +++ extensions/Meta.php | 72 + extensions/RSS.php | 88 + extensions/RedCloth.php | 24 + extensions/RedCloth/redcloth | 6 + extensions/RedCloth/redcloth.rb | 1113 ++++++++++ extensions/extexception.php | 53 + helpers/application_helper.php | 254 +++ library/AutoLoad.php | 14 + library/Canvas.php | 27 + library/Config2.php | 108 + library/Controller.php | 299 +++ library/Conventions.php | 246 +++ library/Debug.php | 146 ++ library/File.php | 154 ++ library/Globals.php | 45 + library/LICENSE | 36 + library/Model2.php | 457 ++++ library/README | 7 + library/Request.php | 163 ++ library/Response.php | 30 + library/Router.php | 102 + library/Router2.php | 173 ++ library/Session.php | 76 + library/View.php | 78 + library/YAML.php | 25 + library/YAML/CHANGES | 45 + library/YAML/README | 162 ++ library/YAML/spyc.php | 853 ++++++++ library/YAML/spyc.php4 | 853 ++++++++ library/YAML/spyc.yml | 106 + library/YAML/test.php | 152 ++ library/YAML/yaml-dump.php | 36 + library/YAML/yaml-load.php | 23 + library/adapters/mysql.php | 132 ++ library/adapters/mysql_backup.php | 320 +++ library/database.php | 409 ++++ library/dependencies.php | 62 + library/ext/Pager.php | 117 + library/ext/Pluralize.php | 478 ++++ library/interfaces/Adapter.php | 14 + library/interfaces/Model.php | 15 + library/interfaces/_adapter.php | 14 + library/smarty/Config_File.class.php | 389 ++++ library/smarty/Smarty.class.php | 1941 ++++++++++++++++ library/smarty/Smarty_Compiler.class.php | 2311 ++++++++++++++++++++ library/smarty/debug.tpl | 64 + .../internals/core.assemble_plugin_filepath.php | 67 + .../internals/core.assign_smarty_interface.php | 43 + .../smarty/internals/core.create_dir_structure.php | 79 + .../internals/core.display_debug_console.php | 61 + library/smarty/internals/core.get_include_path.php | 44 + library/smarty/internals/core.get_microtime.php | 23 + library/smarty/internals/core.get_php_resource.php | 80 + library/smarty/internals/core.is_secure.php | 59 + library/smarty/internals/core.is_trusted.php | 47 + library/smarty/internals/core.load_plugins.php | 125 ++ .../smarty/internals/core.load_resource_plugin.php | 74 + .../internals/core.process_cached_inserts.php | 71 + .../internals/core.process_compiled_include.php | 37 + library/smarty/internals/core.read_cache_file.php | 101 + library/smarty/internals/core.rm_auto.php | 71 + library/smarty/internals/core.rmdir.php | 54 + .../smarty/internals/core.run_insert_handler.php | 71 + .../smarty/internals/core.smarty_include_php.php | 50 + library/smarty/internals/core.write_cache_file.php | 96 + .../internals/core.write_compiled_include.php | 91 + .../internals/core.write_compiled_resource.php | 35 + library/smarty/internals/core.write_file.php | 54 + library/smarty/plugins/block.textformat.php | 103 + library/smarty/plugins/compiler.assign.php | 40 + .../smarty/plugins/function.assign_debug_info.php | 40 + library/smarty/plugins/function.config_load.php | 142 ++ library/smarty/plugins/function.counter.php | 80 + library/smarty/plugins/function.cycle.php | 102 + library/smarty/plugins/function.debug.php | 35 + library/smarty/plugins/function.eval.php | 49 + library/smarty/plugins/function.fetch.php | 221 ++ .../smarty/plugins/function.html_checkboxes.php | 143 ++ library/smarty/plugins/function.html_image.php | 142 ++ library/smarty/plugins/function.html_options.php | 122 ++ library/smarty/plugins/function.html_radios.php | 156 ++ .../smarty/plugins/function.html_select_date.php | 323 +++ .../smarty/plugins/function.html_select_time.php | 194 ++ library/smarty/plugins/function.html_table.php | 137 ++ library/smarty/plugins/function.mailto.php | 163 ++ library/smarty/plugins/function.math.php | 84 + library/smarty/plugins/function.popup.php | 119 + library/smarty/plugins/function.popup_init.php | 40 + library/smarty/plugins/modifier.capitalize.php | 43 + library/smarty/plugins/modifier.cat.php | 33 + .../smarty/plugins/modifier.count_characters.php | 32 + .../smarty/plugins/modifier.count_paragraphs.php | 29 + .../smarty/plugins/modifier.count_sentences.php | 29 + library/smarty/plugins/modifier.count_words.php | 33 + library/smarty/plugins/modifier.date_format.php | 49 + .../smarty/plugins/modifier.debug_print_var.php | 57 + library/smarty/plugins/modifier.default.php | 32 + library/smarty/plugins/modifier.escape.php | 93 + library/smarty/plugins/modifier.indent.php | 28 + library/smarty/plugins/modifier.lower.php | 26 + library/smarty/plugins/modifier.nl2br.php | 35 + library/smarty/plugins/modifier.regex_replace.php | 34 + library/smarty/plugins/modifier.replace.php | 30 + library/smarty/plugins/modifier.spacify.php | 30 + library/smarty/plugins/modifier.string_format.php | 29 + library/smarty/plugins/modifier.strip.php | 33 + library/smarty/plugins/modifier.strip_tags.php | 32 + library/smarty/plugins/modifier.truncate.php | 50 + library/smarty/plugins/modifier.upper.php | 26 + library/smarty/plugins/modifier.wordwrap.php | 29 + .../smarty/plugins/outputfilter.trimwhitespace.php | 75 + .../smarty/plugins/shared.escape_special_chars.php | 31 + library/smarty/plugins/shared.make_timestamp.php | 46 + library/stdexception.php | 53 + logs/.htaccess | 5 + logs/dev.log | 0 logs/production.log | 0 logs/sql.log | 0 logs/sql.warn.log | 0 logs/system.log | 0 logs/test.log | 0 res/.htaccess | 1 + res/js/builder.js | 101 + res/js/controls.js | 815 +++++++ res/js/dragdrop.js | 915 ++++++++ res/js/effects.js | 958 ++++++++ res/js/lightbox.js | 173 ++ res/js/moo.fx.js | 119 + res/js/moo.fx.pack.js | 241 ++ res/js/prototype.js | 2006 +++++++++++++++++ res/js/prototype.lb.js | 1785 +++++++++++++++ res/js/prototype.lite.js | 127 ++ res/js/scriptaculous.js | 47 + res/js/slider.js | 283 +++ res/js/unittest.js | 383 ++++ scripts/.htaccess | 5 + scripts/generate | 16 + scripts/generator/actions.rb | 70 + scripts/generator/base.rb | 29 + scripts/generator/templates/controller.rhtml | 38 + scripts/generator/templates/model.rhtml | 44 + scripts/generator/templates/view.rhtml | 0 scripts/generator/templates/view_layout.rhtml | 8 + scripts/set_permissions | 20 + static/.DS_Store | Bin 0 -> 6148 bytes static/.htaccess | 6 + static/errors/.DS_Store | Bin 0 -> 6148 bytes static/errors/403.html | 1 + static/images/.DS_Store | Bin 0 -> 6148 bytes static/images/.htaccess | 2 + views/templates/ajax_response.php | 18 + views/templates/layout.php | 68 + 162 files changed, 26105 insertions(+) create mode 100755 config/.htaccess create mode 100644 config/config.cache create mode 100755 config/config.yml create mode 100755 config/database.yml create mode 100755 config/routes.php create mode 100755 controllers/application_controller.php create mode 100755 dispatcher.php create mode 100755 extensions/Auth.php create mode 100755 extensions/Auth2.php create mode 100644 extensions/LDAP.php create mode 100755 extensions/Meta.php create mode 100644 extensions/RSS.php create mode 100755 extensions/RedCloth.php create mode 100755 extensions/RedCloth/redcloth create mode 100755 extensions/RedCloth/redcloth.rb create mode 100755 extensions/extexception.php create mode 100644 helpers/application_helper.php create mode 100755 library/AutoLoad.php create mode 100755 library/Canvas.php create mode 100755 library/Config2.php create mode 100644 library/Controller.php create mode 100755 library/Conventions.php create mode 100755 library/Debug.php create mode 100755 library/File.php create mode 100755 library/Globals.php create mode 100644 library/LICENSE create mode 100644 library/Model2.php create mode 100644 library/README create mode 100755 library/Request.php create mode 100755 library/Response.php create mode 100755 library/Router.php create mode 100644 library/Router2.php create mode 100755 library/Session.php create mode 100755 library/View.php create mode 100755 library/YAML.php create mode 100755 library/YAML/CHANGES create mode 100755 library/YAML/README create mode 100755 library/YAML/spyc.php create mode 100755 library/YAML/spyc.php4 create mode 100755 library/YAML/spyc.yml create mode 100755 library/YAML/test.php create mode 100755 library/YAML/yaml-dump.php create mode 100755 library/YAML/yaml-load.php create mode 100755 library/adapters/mysql.php create mode 100755 library/adapters/mysql_backup.php create mode 100755 library/database.php create mode 100644 library/dependencies.php create mode 100755 library/ext/Pager.php create mode 100755 library/ext/Pluralize.php create mode 100755 library/interfaces/Adapter.php create mode 100755 library/interfaces/Model.php create mode 100755 library/interfaces/_adapter.php create mode 100755 library/smarty/Config_File.class.php create mode 100755 library/smarty/Smarty.class.php create mode 100755 library/smarty/Smarty_Compiler.class.php create mode 100755 library/smarty/debug.tpl create mode 100755 library/smarty/internals/core.assemble_plugin_filepath.php create mode 100755 library/smarty/internals/core.assign_smarty_interface.php create mode 100755 library/smarty/internals/core.create_dir_structure.php create mode 100755 library/smarty/internals/core.display_debug_console.php create mode 100755 library/smarty/internals/core.get_include_path.php create mode 100755 library/smarty/internals/core.get_microtime.php create mode 100755 library/smarty/internals/core.get_php_resource.php create mode 100755 library/smarty/internals/core.is_secure.php create mode 100755 library/smarty/internals/core.is_trusted.php create mode 100755 library/smarty/internals/core.load_plugins.php create mode 100755 library/smarty/internals/core.load_resource_plugin.php create mode 100755 library/smarty/internals/core.process_cached_inserts.php create mode 100755 library/smarty/internals/core.process_compiled_include.php create mode 100755 library/smarty/internals/core.read_cache_file.php create mode 100755 library/smarty/internals/core.rm_auto.php create mode 100755 library/smarty/internals/core.rmdir.php create mode 100755 library/smarty/internals/core.run_insert_handler.php create mode 100755 library/smarty/internals/core.smarty_include_php.php create mode 100755 library/smarty/internals/core.write_cache_file.php create mode 100755 library/smarty/internals/core.write_compiled_include.php create mode 100755 library/smarty/internals/core.write_compiled_resource.php create mode 100755 library/smarty/internals/core.write_file.php create mode 100755 library/smarty/plugins/block.textformat.php create mode 100755 library/smarty/plugins/compiler.assign.php create mode 100755 library/smarty/plugins/function.assign_debug_info.php create mode 100755 library/smarty/plugins/function.config_load.php create mode 100755 library/smarty/plugins/function.counter.php create mode 100755 library/smarty/plugins/function.cycle.php create mode 100755 library/smarty/plugins/function.debug.php create mode 100755 library/smarty/plugins/function.eval.php create mode 100755 library/smarty/plugins/function.fetch.php create mode 100755 library/smarty/plugins/function.html_checkboxes.php create mode 100755 library/smarty/plugins/function.html_image.php create mode 100755 library/smarty/plugins/function.html_options.php create mode 100755 library/smarty/plugins/function.html_radios.php create mode 100755 library/smarty/plugins/function.html_select_date.php create mode 100755 library/smarty/plugins/function.html_select_time.php create mode 100755 library/smarty/plugins/function.html_table.php create mode 100755 library/smarty/plugins/function.mailto.php create mode 100755 library/smarty/plugins/function.math.php create mode 100755 library/smarty/plugins/function.popup.php create mode 100755 library/smarty/plugins/function.popup_init.php create mode 100755 library/smarty/plugins/modifier.capitalize.php create mode 100755 library/smarty/plugins/modifier.cat.php create mode 100755 library/smarty/plugins/modifier.count_characters.php create mode 100755 library/smarty/plugins/modifier.count_paragraphs.php create mode 100755 library/smarty/plugins/modifier.count_sentences.php create mode 100755 library/smarty/plugins/modifier.count_words.php create mode 100755 library/smarty/plugins/modifier.date_format.php create mode 100755 library/smarty/plugins/modifier.debug_print_var.php create mode 100755 library/smarty/plugins/modifier.default.php create mode 100755 library/smarty/plugins/modifier.escape.php create mode 100755 library/smarty/plugins/modifier.indent.php create mode 100755 library/smarty/plugins/modifier.lower.php create mode 100755 library/smarty/plugins/modifier.nl2br.php create mode 100755 library/smarty/plugins/modifier.regex_replace.php create mode 100755 library/smarty/plugins/modifier.replace.php create mode 100755 library/smarty/plugins/modifier.spacify.php create mode 100755 library/smarty/plugins/modifier.string_format.php create mode 100755 library/smarty/plugins/modifier.strip.php create mode 100755 library/smarty/plugins/modifier.strip_tags.php create mode 100755 library/smarty/plugins/modifier.truncate.php create mode 100755 library/smarty/plugins/modifier.upper.php create mode 100755 library/smarty/plugins/modifier.wordwrap.php create mode 100755 library/smarty/plugins/outputfilter.trimwhitespace.php create mode 100755 library/smarty/plugins/shared.escape_special_chars.php create mode 100755 library/smarty/plugins/shared.make_timestamp.php create mode 100755 library/stdexception.php create mode 100644 logs/.htaccess create mode 100644 logs/dev.log create mode 100644 logs/production.log create mode 100644 logs/sql.log create mode 100755 logs/sql.warn.log create mode 100644 logs/system.log create mode 100755 logs/test.log create mode 100755 res/.htaccess create mode 100644 res/js/builder.js create mode 100644 res/js/controls.js create mode 100644 res/js/dragdrop.js create mode 100644 res/js/effects.js create mode 100755 res/js/lightbox.js create mode 100644 res/js/moo.fx.js create mode 100644 res/js/moo.fx.pack.js create mode 100644 res/js/prototype.js create mode 100755 res/js/prototype.lb.js create mode 100644 res/js/prototype.lite.js create mode 100644 res/js/scriptaculous.js create mode 100644 res/js/slider.js create mode 100644 res/js/unittest.js create mode 100644 scripts/.htaccess create mode 100755 scripts/generate create mode 100644 scripts/generator/actions.rb create mode 100644 scripts/generator/base.rb create mode 100755 scripts/generator/templates/controller.rhtml create mode 100644 scripts/generator/templates/model.rhtml create mode 100644 scripts/generator/templates/view.rhtml create mode 100644 scripts/generator/templates/view_layout.rhtml create mode 100755 scripts/set_permissions create mode 100644 static/.DS_Store create mode 100755 static/.htaccess create mode 100644 static/errors/.DS_Store create mode 100755 static/errors/403.html create mode 100644 static/images/.DS_Store create mode 100644 static/images/.htaccess create mode 100755 views/templates/ajax_response.php create mode 100644 views/templates/layout.php diff --git a/config/.htaccess b/config/.htaccess new file mode 100755 index 0000000..1cc28ee --- /dev/null +++ b/config/.htaccess @@ -0,0 +1,5 @@ +Order Deny,Allow +Deny from All + +# try to abstract this so that you don't have to put the full address here +ErrorDocument 403 /path/to/403/file \ No newline at end of file diff --git a/config/config.cache b/config/config.cache new file mode 100644 index 0000000..e69de29 diff --git a/config/config.yml b/config/config.yml new file mode 100755 index 0000000..7779fb7 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,19 @@ +environment: dev +adapter: MySQL + +project: + project: Project Name + version: 0.0.1 + group: Group Name Here + department: Department Name Here + organization: Organization Or Company Name Here + +directories: + logs: logs + +logging: + log_level: warn +# always_log: +# - internal +# log_separately: +# - sql diff --git a/config/database.yml b/config/database.yml new file mode 100755 index 0000000..fbf3739 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,20 @@ +production: + adapter : MySQL + host : localhost + database : database + username : username + password : password + +test: + adapter : MySQL + host : localhost + database : database + username : username + password : password + +dev: + adapter : MySQL + host : localhost + database : database + username : username + password : password diff --git a/config/routes.php b/config/routes.php new file mode 100755 index 0000000..c3610f0 --- /dev/null +++ b/config/routes.php @@ -0,0 +1,33 @@ + + // @created 2005-12-23 + // @desc The routing patterns to match the request against to set the appropriate request variables + + // setup date routes for searching times + Router2::map(':year/:month/:day', + array('controller'=>'archive', 'action'=>'timespan'), + array('year'=>'year', 'month'=>'month', 'day'=>'day')); + Router2::map(':year/:month', + array('controller'=>'archive', 'action'=>'timespan'), + array('year'=>'year', 'month'=>'month')); + Router2::map(':year', + array('controller'=>'archive', 'action'=>'timespan'), + array('year'=>'year')); + + // map multiple tags to the blog/tags controller/action + Router2::map('tags/:tags*', array('controller'=>'blog', 'action'=>'tags')); + + // flexi-generic route (for show/:id or maybe comment/:id, etc) + Router2::map(':action/:id', array('controller'=>'blog')); + + // flexi-generic route (for :controller/:action/:id) + Router2::map(':controller/:action/:id'); + + // flexi-generic route (for show/:id or maybe comment/:id, etc) + Router2::map(':action/comment/:id/from/post/:post_id', array('controller'=>'comments')); + + // default to the blog/index controller/action + Router2::map('', array('controller'=>'blog', 'action'=>'index')); + +?> \ No newline at end of file diff --git a/controllers/application_controller.php b/controllers/application_controller.php new file mode 100755 index 0000000..dae1586 --- /dev/null +++ b/controllers/application_controller.php @@ -0,0 +1,122 @@ +'require_login', 'PostNotFound'=>'post_not_found', '*'=>'exception_handler'); + + protected $default_controller = 'blog'; + protected $default_action = 'index'; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // exception handling ///////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + protected function exception_handler($e) { + // log error, set flash, and redirect to index + Debug::log($e->getMessage(), 'exception_handler', 'error', get_class($this)); + $this->flash = array('message'=>'Error: ' . $e->getMessage(), 'class'=>'Bad'); + $this->redirect_to(array('controller'=>$this->default_controller, 'action'=>$this->default_action)); + + // Debug::generic_exception_handler($e); // this is ugly, and only for development! + } + + // specific exception handling //////////////////////////////////////////////////////////////////////////// + + protected function require_login($e) { + $this->session->continue_to = $this->request->url; + $this->flash = array('message'=>$e->getMessage(), 'class'=>'Bad'); + $this->redirect_to(array('controller'=>$this->default_controller, 'action'=>$this->default_action)); + } + + protected function post_not_found($e) { + $this->flash = array('message'=>$e->getMessage(), 'class'=>'Bad'); + $this->redirect_to(array('controller'=>$this->default_controller, 'action'=>$this->default_action)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // authentication ///////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // global authentication and session handler + protected function authenticate() { + // check for previous user authentication + $user = new user(); + $user->find_by_username_and_password($this->session->auth->username, $this->session->auth->password); + + if(!$user->is_empty()) { + // authenticated + $this->session->auth = $user; // array('user'=>array('username'=>$user->username, 'password'=>$user->password, 'id'=>$user->id, 'role'=>$user->role)); + $this->user = $user; + return true; + } else { + // just skip authentication + return true; + } + } + + public function login() { + // if login form has been submitted, process... otherwise, display the login form/page + $login = $this->request->post->login; + if(empty($login)) { + + // send to the index page + $this->flash('...', 'Info'); + $this->session->continue_to = $this->request->continue_to; // remembering continuation + $this->redirect_to(array('controller'=>$this->default_controller, 'action'=>$this->default_action)); + + } else { + + // authenticate user + $user = new user(); + if(!$user->find_by_username_and_password($login['username'], md5($login['password']))->is_empty()) { + // store user data in session + $this->session->auth = $user; + $this->user = $user; + + // display 'logged in' on the next (visible) page load + $this->flash('Logged in!', 'Good'); + + // if there was a previous request, go there instead of the main index + if(!empty($this->request->continue_to)) { + $this->redirect_to(array('url'=>$this->request->continue_to)); + } else + $this->redirect_to(array('controller'=>$this->default_controller, "action"=>$this->default_action)); + } else { + // did not authenticate, so display error message and take user to login screen + $this->flash("Login failed", "Bad"); + $this->redirect_to(array('controller'=>$this->default_controller, "action"=>$this->default_action)); + + // halt request + die(); + + } + } + } + public function logout() { + // kill the session + Session::destroy(); + + // display 'logged out' and redirect to the login page + $this->flash("Logged out", "Good"); + $this->redirect_to(array('controller'=>$this->default_controller, "action"=>$this->default_action)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // events and callbacks /////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // add in data (after all other processing) for forms, et al + public function after_action() { + // use this to respond to the views with whatever data is necessary (on every page, usually) + } + } + + class NotLoggedIn extends Exception {} + class InsufficientPrivilegesException extends Exception {} +?> \ No newline at end of file diff --git a/dispatcher.php b/dispatcher.php new file mode 100755 index 0000000..a10c09b --- /dev/null +++ b/dispatcher.php @@ -0,0 +1,58 @@ +controller); + $helper_name = Conventions::helper_name($request->controller); + $action_name = Conventions::action_name($request->action); + $action_id = $request->id; + + { // handle instanciating request and executing requested function + // include controller classes + if(file_exists(Conventions::controller_path($request->controller))) + include_once Conventions::controller_path($request->controller); + else + Debug::generic_exception_handler(new Exception("'{$controller_name}' does not exist")); + // includ helper (application or controller-specific) + if(file_exists(Conventions::helper_path($request->controller))) include_once Conventions::helper_path($request->controller); else $helper_name = "application_helper"; + + // instanciate controller + $controller = new $controller_name($request); + + // dispatch action + $controller->dispatch($action_name, $action_id, $request); + } + } catch(Exception $e) { + print_r($e); + } + + // timing + $execution_time = round(microtime(true) - $GLOBALS['dispatch_time_begin'], 5); + $execution_memory = round(memory_get_usage()/1024, 2); + Debug::log("Dispatched {$_SERVER['PATH_INFO']} ({$execution_time}sec/{$execution_memory}kb)", 'internal', 'notice', 'Dispatcher'); + +?> \ No newline at end of file diff --git a/extensions/Auth.php b/extensions/Auth.php new file mode 100755 index 0000000..fdc0270 --- /dev/null +++ b/extensions/Auth.php @@ -0,0 +1,67 @@ + + // @created 2005-12-22 + // @desc Handles authentication. Simple, no? However, this needs to be + // altered to integrate with the current authentication system + // @requires stdexception.php (StdException class) + // @requires modles/user.php (User model) + + include_once 'extexception.php'; + + // classes + class Auth { + // functions + public static function authenticate($username, $password) { + // LDAP authentication for username and password + } + + public static function find_login_or_session_data(&$username, &$password) { + // retreive the current session + $session = Session::retreive(); + $session_auth = $session->auth; + + if(!empty($session_auth)) { + $login = $session->auth; + } elseif(!empty($_POST['login'])) { + $login = $_POST['login']; + // Make an MD5 hash of the password from the form: + // this is a security risk if we just execute a plain query + // with the password from the form because the password + // will be stored in the logs (yikes!). + // Plus, it reduces it down to one query, either from + // the login form or from sessions! + $login['password'] = md5($login['password']); + } else { + return false; + } + + $username = $login['username']; + $password = $login['password']; + + return true; + } + + public static function authenticated() { + $session = Session::retreive(); + $auth = $session->auth; + if(!empty($auth)) return true; + return false; + } + + public static function check_role($username, $role) { + $user = new user(); + + try { + $user->find_by_username($username); + } catch(Exception $e) { + return false; + } + + if($user->role['role'] == $role) return true; + return false; + } + } + + class AuthException extends ExtException {} +?> \ No newline at end of file diff --git a/extensions/Auth2.php b/extensions/Auth2.php new file mode 100755 index 0000000..722e75e --- /dev/null +++ b/extensions/Auth2.php @@ -0,0 +1,46 @@ +auth['username']; + $session_password = $session->auth['password']; + // if it's not in the session data, get it from the login form + $username = !empty($session_username) ? $session_username : $_POST['login']['username']; + $password = !empty($session_password) ? $session_password : md5($_POST['login']['password']); + + // determine if previously authenticated (in session) + $ldap = new LDAP(); + if($ldap->find($username, $password)) { + $session->auth['username'] = $username; + $session->auth['password'] = $password; + + return true; + } else { + return false; + } + } + + // checks privileges + public static function check_role($username, $role) { + $user = new user(); + + try { + $user->find_by_username($username); + } catch(Exception $e) { + return false; + } + + if($user->role['role'] == $role) return true; + return false; + } + } +?> \ No newline at end of file diff --git a/extensions/LDAP.php b/extensions/LDAP.php new file mode 100644 index 0000000..31e0e7a --- /dev/null +++ b/extensions/LDAP.php @@ -0,0 +1,274 @@ + + // @created 2005-11-28 + // @desc Handles LDAP authentication and information retreival + + /* @usage + + // show one completed + + */ + + class LDAP { + // properties + public $is_connected = false; + public $is_bound = false; + private $handlers = array(); + private $results = array(); + public $count = 0; + private $results_order = array(); + + // constructor + public function __construct($config = null) { + $this->config = (empty($config) ? Config2::$config['adapters']['ldap'] : $config); + } + + // accessors + public function __get($name) { + $entry = current($this->results); + return $entry[$name]; + } + public function as_array() { + return $this->results; + } + + // iteration and navigation + public function current() { + return current($this->results); + } + public function next() { + next($this->results); + return $this; + } + public function previous() { + prev($this->results); + return $this; + } + public function first() { + reset($this->results); + return $this; + } + public function last() { + end($this->results); + return $this; + } + + // data functions + protected function full_domain($username = null) { + foreach($this->config['domain']['offices'] as $office) { // assemble offices + if(!empty($offices)) $offices .= ','; + $offices .= sprintf('OU=%s', $office); + } + if($username === null) return "{$offices},DC={$this->config[domain][subdomain]},DC={$this->config[domain][domain]},DC={$this->config[domain][top_level_domain]}"; + return "CN={$username},{$offices},DC={$this->config[domain][subdomain]},DC={$this->config[domain][domain]},DC={$this->config[domain][top_level_domain]}"; + } + protected function short_domain($username = null) { + if($username === null) return "{$this->config[domain][short_domain]}"; + return "{$username}@{$this->config[domain][short_domain]}"; + } + + // clean results to a sensible structure + protected function clean($results) { + if(is_array($results)) { + // remove 'count' keys + if(!empty($results['count'])) unset($results['count']); + + // if results has only one value, return it + if((count($results) == 1) && (!empty($results[0])) && (!is_array($results[0]))) { + return $results[0]; + } + + // cleans repetitive data, et al + foreach($results as $key=>$entry) { + # (int)0 == "count", so we need to use === + if($k = array_search($key, $results)) unset($results[$k]); + + // remove all integer keys except for those with valuable data + if(is_int($key) && is_string($entry) && is_array($results[$entry])) { + unset($results[$key]); + continue; + } + + // clean children too + if(is_array($entry)) { + $results[$key] = $this->clean($entry); + } + } + } + // return data + return $results; + } + + // overridden functions + public function connect() { + // connect to LDAP server + if(!($this->handlers['connection'] = ldap_connect($this->config['server']))) { // , 636 + // could not connect to the LDAP server, throw error + throw new Exception("Could not connect to LDAP server {$this->config[server]}"); + } else { + // ldap_set_option($this->handlers['connection'], LDAP_OPT_PROTOCOL_VERSION, 3); + // set connection status + $this->is_connected = true; + return true; + } + } + public function user_bind($username, $password, $full_domain = false) { + // get proper username + // $username = (($full_domain) ? $this->full_domain($username) : $this->short_domain($username)); + + // set default bind status + $bound = false; + + // bind to LDAP server + // this takes the provided departments and if one level doesn't work, removes it and attempts to bind again + while($this->config['domain']['offices']) { + if($this->handlers['binding'] = @ldap_bind($this->handlers['connection'], $this->full_domain($username), $password)) { + $bound = true; + break; + } else { + array_shift($this->config['domain']['offices']); + } + } + if(!$bound) { + // could not bind to the server as $username + throw new Exception("Could not bind the LDAP server as {$username}"); + } else { + // successfully connected and bound server as $username + $this->is_bound = true; + return true; + } + } + public function bind($full_domain = false) { + // ldap reader binding + $username = $this->config['domain']['reader']['username']; + $password = $this->config['domain']['reader']['password']; + + // bind to LDAP server + if(!($this->handlers['binding'] = @ldap_bind($this->handlers['connection'], $username, $password))) { + // could not bind to the server as $username + throw new Exception("Could not bind the LDAP server as {$username}"); + } else { + // successfully connected and bound server as $username + $this->is_bound = true; + return true; + } + } + public function disconnect() { + // unbind/disconnect LDAP server + if(!(ldap_unbind($this->handlers['connection']))) return false; + + $this->is_bound = false; + $this->is_connected = false; + $this->handlers['connection'] = null; + + return true; + } + public function find($params, $full_domain = false) { + // remove previous results + $this->handlers['results'] = null; + + // set default params + $restrict_to = array(); + $filter = array(); + $sort = array(); + + // get settings from config + if(!empty($this->config['restrict_to'])) $restrict_to = $this->config['restrict_to']; + if(!empty($this->config['filter'])) $filter = $this->config['filter']; + if(!empty($this->config['sort'])) $sort = $this->config['sort']; + + // get parameters for search + $domain = (($full_domain) ? $this->full_domain() : $this->short_domain()); + if(!empty($params['filter'])) $filter = $params['filter']; // such as: "(|(sn=$person*)(givenname=$person*))"; + if(!empty($params['restrict_to'])) $restrict_to = $params['restrict_to']; // such as: array("ou", "sn", "givenname", "mail"); + if(!empty($params['sort'])) $sort = $params['sort']; // such as: array("department", "sn"); + + // find + $this->handlers['results'] = @ldap_search($this->handlers['connection'], $domain, $filter, $restrict_to); + + // if sort param set, sort results + if(!empty($sort)) { + foreach($sort as $key) { + @ldap_sort($this->handlers['connection'], $this->handlers['results'], $key); + } + } + + // get entries + $results = @ldap_get_entries($this->handlers['connection'], $this->handlers['results']); + + // get count + $this->count = $results['count']; + + // clean results + $results = $this->clean($results); + + // process results + foreach($results as $key=>$entry) { + $this->results_order[$key] = $entry['name']; + $this->results[$entry['name']] = new entry($entry); + } + + // remove results handle + $this->handlers['results'] = array(); + + // return this object for more actions + return $this; + } + public function locate($name) { + return $this->results[$name]; + } + public function save($entry, $params = null, $full_domain = false) { + // save entry + } + public function insert($params, $entry, $full_domain = false) { + // get domain + if(empty($params['domain'])) $domain = (($full_domain) ? $this->full_domain() : $this->short_domain()); else $domain = $params['domain']; + + // insert data + if(!(ldap_add($this->handlers['connection'], $domain, $entry))) return false; + + return true; + } + public function update($params, $entry, $full_domain = false) { + // get domain + if(empty($params['domain'])) $domain = (($full_domain) ? $this->full_domain() : $this->short_domain()); else $domain = $params['domain']; + + // insert data + if(!(ldap_modify($this->handlers['connection'], $domain, $entry))) return false; + + return true; + } + public function delete($params, $full_domain = false) { + // get domain + if(empty($params['domain'])) $domain = (($full_domain) ? $this->full_domain() : $this->short_domain()); else $domain = $params['domain']; + + // insert data + if(!(ldap_delete($this->handlers['connection'], $domain))) return false; + + return true; + } + + // retrieve LDAP error on failures + public function error() { + return ldap_error($this->handlers['connection']); + } + } + + class entry { + private $properties = array(); + + // constructor + public function __construct($properties) { + $this->properties = $properties; + } + + // accessors + public function __get($name) { + return $this->properties[$name]; + } + public function as_array() { + return $this->properties; + } + } +?> \ No newline at end of file diff --git a/extensions/Meta.php b/extensions/Meta.php new file mode 100755 index 0000000..3af4e28 --- /dev/null +++ b/extensions/Meta.php @@ -0,0 +1,72 @@ + + // @created 2005-12-22 + // @desc Handles authentication. Simple, no? However, this needs to be + // altered to integrate with the current authentication system + // @requires stdexception.php (StdException class) + // @requires modles/user.php (User model) + + include_once('extexception.php'); + + // classes + class Meta { + // record activity + public static function record_activity($activity, $file_id = null, $accessed_at = null) { + // record activity + $meta_file = new meta_file(); + if($file_id != null) $meta_file->file_id = $file_id; + $meta_file->user_id = self::session_user_id(); + $meta_file->activity_id = activity::find_activity_id($activity); + $meta_file->accessed_at = ($accessed_at == null) ? date('Y-m-d H:i:s') : $accessed_at; + $meta_file->save(); + } + public static function login() { + // record login activity + self::record_activity('login'); + } + + public static function logout() { + // record logout activity + self::record_activity('logout'); + } + + public static function download($file_id) { + // record logout activity + self::record_activity('download', $file_id); + } + + public static function upload($file_id) { + // record logout activity + self::record_activity('upload', $file_id); + } + + public static function update($file_id) { + // record logout activity + self::record_activity('update', $file_id); + } + + public static function approve($file_id) { + // record file approval + self::record_activity('approve', $file_id); + } + + public static function disable($file_id) { + // record file disabling + self::record_activity('disable', $file_id); + } + + public static function delete($file_id) { + // record file deletion + self::record_activity('delete', $file_id); + } + + // get session data + private static function session_user_id() { + $session = Session::retreive(); + return $session->auth->id; + } + } + + class MetaException extends ExtException {} // shouldn't have to be used +?> \ No newline at end of file diff --git a/extensions/RSS.php b/extensions/RSS.php new file mode 100644 index 0000000..d47544a --- /dev/null +++ b/extensions/RSS.php @@ -0,0 +1,88 @@ + + // @created_on 29 Apr 2006 + + class RSS { + // internal static variables + public static $version_templates = array( + 'rss20'=>array(), // RSS2.0 templates + ); + + // internal instance variables + private $templates = array(); + private $feed = ''; + private $items = array(); + + // constructor + public function __construct($version = 'rss20', $options = array()) { + // get templates + $this->templates = self::$version_templates[$version]; + + // process data into templates + $feed = $this->templates['body']; + foreach($options as $key=>$option) { + $feed = str_replace(":{$key}", $option, $feed); + } + + // update the feed data + $this->feed = $feed; + } + + // route request + public function add($item) { + // get template + $item_template = $this->templates['item']; + + // parse through values + foreach($item as $key=>$value) { + $item_template = str_replace(":{$key}", $value, $item_template); + } + + // store new item + $this->items[] = $item_template; + } + + public function to_string() { + return $this->render(); + } + public function render() { + return str_replace(':items', implode("\n\n", $this->items), $this->feed); + } + } + +RSS::$version_templates['rss20']['body'] = << + + + :title + :link + + :description + + :language + + :items + + + +EOF; +RSS::$version_templates['rss20']['item'] = << + :title + :link + :guid + :author + :pubDate + + + + :comments + +EOF; +?> \ No newline at end of file diff --git a/extensions/RedCloth.php b/extensions/RedCloth.php new file mode 100755 index 0000000..f644c05 --- /dev/null +++ b/extensions/RedCloth.php @@ -0,0 +1,24 @@ + + // @created 2006-01-08 + // @desc Handles formatting (based on Textile) + // @refer_to "RedHanded":http://redhanded.hobix.com/ + // @requires stdexception.php (ExtException class) + + include_once('extexception.php'); + + // classes + class RedCloth { + // create HTML from RedCloth formatting + public static function to_html($input) { + $input = addslashes($input); + $output = `./extensions/RedCloth/redcloth "{$input}"`; + return stripslashes($output); + } + } + + class RedClothException extends ExtException { + } +?> diff --git a/extensions/RedCloth/redcloth b/extensions/RedCloth/redcloth new file mode 100755 index 0000000..c68480e --- /dev/null +++ b/extensions/RedCloth/redcloth @@ -0,0 +1,6 @@ +#!/usr/bin/ruby +require 'extensions/RedCloth/redcloth' +arguments = ARGV.shift +puts RedCloth.new(arguments.split('\\n').join("\n")).to_html +# arguments.split('\\n').each do |line| puts RedCloth.new(line).to_html end +# puts RedCloth.new( ARGF.read ).to_html diff --git a/extensions/RedCloth/redcloth.rb b/extensions/RedCloth/redcloth.rb new file mode 100755 index 0000000..03df12b --- /dev/null +++ b/extensions/RedCloth/redcloth.rb @@ -0,0 +1,1113 @@ +# vim:ts=4:sw=4: +# = RedCloth - Textile and Markdown Hybrid for Ruby +# +# Homepage:: http://whytheluckystiff.net/ruby/redcloth/ +# Author:: why the lucky stiff (http://whytheluckystiff.net/) +# Copyright:: (cc) 2004 why the lucky stiff (and his puppet organizations.) +# License:: BSD +# +# (see http://hobix.com/textile/ for a Textile Reference.) +# +# Based on (and also inspired by) both: +# +# PyTextile: http://diveintomark.org/projects/textile/textile.py.txt +# Textism for PHP: http://www.textism.com/tools/textile/ +# +# + +# = RedCloth +# +# RedCloth is a Ruby library for converting Textile and/or Markdown +# into HTML. You can use either format, intermingled or separately. +# You can also extend RedCloth to honor your own custom text stylings. +# +# RedCloth users are encouraged to use Textile if they are generating +# HTML and to use Markdown if others will be viewing the plain text. +# +# == What is Textile? +# +# Textile is a simple formatting style for text +# documents, loosely based on some HTML conventions. +# +# == Sample Textile Text +# +# h2. This is a title +# +# h3. This is a subhead +# +# This is a bit of paragraph. +# +# bq. This is a blockquote. +# +# = Writing Textile +# +# A Textile document consists of paragraphs. Paragraphs +# can be specially formatted by adding a small instruction +# to the beginning of the paragraph. +# +# h[n]. Header of size [n]. +# bq. Blockquote. +# # Numeric list. +# * Bulleted list. +# +# == Quick Phrase Modifiers +# +# Quick phrase modifiers are also included, to allow formatting +# of small portions of text within a paragraph. +# +# \_emphasis\_ +# \_\_italicized\_\_ +# \*strong\* +# \*\*bold\*\* +# ??citation?? +# -deleted text- +# +inserted text+ +# ^superscript^ +# ~subscript~ +# @code@ +# %(classname)span% +# +# ==notextile== (leave text alone) +# +# == Links +# +# To make a hypertext link, put the link text in "quotation +# marks" followed immediately by a colon and the URL of the link. +# +# Optional: text in (parentheses) following the link text, +# but before the closing quotation mark, will become a Title +# attribute for the link, visible as a tool tip when a cursor is above it. +# +# Example: +# +# "This is a link (This is a title) ":http://www.textism.com +# +# Will become: +# +# This is a link +# +# == Images +# +# To insert an image, put the URL for the image inside exclamation marks. +# +# Optional: text that immediately follows the URL in (parentheses) will +# be used as the Alt text for the image. Images on the web should always +# have descriptive Alt text for the benefit of readers using non-graphical +# browsers. +# +# Optional: place a colon followed by a URL immediately after the +# closing ! to make the image into a link. +# +# Example: +# +# !http://www.textism.com/common/textist.gif(Textist)! +# +# Will become: +# +# Textist +# +# With a link: +# +# !/common/textist.gif(Textist)!:http://textism.com +# +# Will become: +# +# Textist +# +# == Defining Acronyms +# +# HTML allows authors to define acronyms via the tag. The definition appears as a +# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing, +# this should be used at least once for each acronym in documents where they appear. +# +# To quickly define an acronym in Textile, place the full text in (parentheses) +# immediately following the acronym. +# +# Example: +# +# ACLU(American Civil Liberties Union) +# +# Will become: +# +# ACLU +# +# == Adding Tables +# +# In Textile, simple tables can be added by seperating each column by +# a pipe. +# +# |a|simple|table|row| +# |And|Another|table|row| +# +# Attributes are defined by style definitions in parentheses. +# +# table(border:1px solid black). +# (background:#ddd;color:red). |{}| | | | +# +# == Using RedCloth +# +# RedCloth is simply an extension of the String class, which can handle +# Textile formatting. Use it like a String and output HTML with its +# RedCloth#to_html method. +# +# doc = RedCloth.new " +# +# h2. Test document +# +# Just a simple test." +# +# puts doc.to_html +# +# By default, RedCloth uses both Textile and Markdown formatting, with +# Textile formatting taking precedence. If you want to turn off Markdown +# formatting, to boost speed and limit the processor: +# +# class RedCloth::Textile.new( str ) + +class RedCloth < String + + VERSION = '3.0.3' + DEFAULT_RULES = [:textile, :markdown] + + # + # Two accessor for setting security restrictions. + # + # This is a nice thing if you're using RedCloth for + # formatting in public places (e.g. Wikis) where you + # don't want users to abuse HTML for bad things. + # + # If +:filter_html+ is set, HTML which wasn't + # created by the Textile processor will be escaped. + # + # If +:filter_styles+ is set, it will also disable + # the style markup specifier. ('{color: red}') + # + attr_accessor :filter_html, :filter_styles + + # + # Accessor for toggling hard breaks. + # + # If +:hard_breaks+ is set, single newlines will + # be converted to HTML break tags. This is the + # default behavior for traditional RedCloth. + # + attr_accessor :hard_breaks + + # + # Accessor for toggling span caps. + # + # Textile places `span' tags around capitalized + # words by default, but this wreaks havoc on Wikis. + # If +:no_span_caps+ is set, this will be + # suppressed. + # + attr_accessor :no_span_caps + + # + # Establishes the markup predence. Available rules include: + # + # == Textile Rules + # + # The following textile rules can be set individually. Or add the complete + # set of rules with the single :textile rule, which supplies the rule set in + # the following precedence: + # + # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/) + # block_textile_table:: Textile table block structures + # block_textile_lists:: Textile list structures + # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.) + # inline_textile_image:: Textile inline images + # inline_textile_link:: Textile inline links + # inline_textile_span:: Textile inline spans + # inline_textile_glyphs:: Textile entities (such as em-dashes and smart quotes) + # + # == Markdown + # + # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/) + # block_markdown_setext:: Markdown setext headers + # block_markdown_atx:: Markdown atx headers + # block_markdown_rule:: Markdown horizontal rules + # block_markdown_bq:: Markdown blockquotes + # block_markdown_lists:: Markdown lists + # inline_markdown_link:: Markdown links + attr_accessor :rules + + # Returns a new RedCloth object, based on _string_ and + # enforcing all the included _restrictions_. + # + # r = RedCloth.new( "h1. A bold man", [:filter_html] ) + # r.to_html + # #=>"

A <b>bold</b> man

" + # + def initialize( string, restrictions = [] ) + restrictions.each { |r| method( "#{ r }=" ).call( true ) } + super( string ) + end + + # + # Generates HTML from the Textile contents. + # + # r = RedCloth.new( "And then? She *fell*!" ) + # r.to_html( true ) + # #=>"And then? She fell!" + # + def to_html( *rules ) + rules = DEFAULT_RULES if rules.empty? + # make our working copy + text = self.dup + + @urlrefs = {} + @shelf = [] + textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists, + :block_textile_prefix, :inline_textile_image, :inline_textile_link, + :inline_textile_code, :inline_textile_glyphs, :inline_textile_span] + markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule, + :block_markdown_bq, :block_markdown_lists, + :inline_markdown_reflink, :inline_markdown_link] + @rules = rules.collect do |rule| + case rule + when :markdown + markdown_rules + when :textile + textile_rules + else + rule + end + end.flatten + + # standard clean up + incoming_entities text + clean_white_space text + no_textile text + + # start processor + @pre_list = [] + rip_offtags text + hard_break text + refs text + blocks text + inline text + smooth_offtags text + + retrieve text + + text.gsub!( /<\/?notextile>/, '' ) + text.gsub!( /x%x%/, '&' ) + clean_html text if filter_html + text.strip! + text + + end + + ####### + private + ####### + # + # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents. + # (from PyTextile) + # + TEXTILE_TAGS = + + [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230], + [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249], + [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217], + [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732], + [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]]. + + collect! do |a, b| + [a.chr, ( b.zero? and "" or "&#{ b };" )] + end + + # + # Regular expressions to convert to HTML. + # + A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/ + A_VLGN = /[\-^~]/ + C_CLAS = '(?:\([^)]+\))' + C_LNGE = '(?:\[[^\]]+\])' + C_STYL = '(?:\{[^}]+\})' + S_CSPN = '(?:\\\\\d+)' + S_RSPN = '(?:/\d+)' + A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)" + S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)" + C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)" + # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ) + PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' ) + HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(?=\s|<|$)' + + # Text markup tags, don't conflict with block tags + SIMPLE_HTML_TAGS = [ + 'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code', + 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br', + 'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo' + ] + + # Elements to handle + GLYPHS = [ + # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing + [ /([^\s\[{(>])\'/, '\1’' ], # single closing + [ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing + [ /\'/, '‘' ], # single opening + # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing + [ /([^\s\[{(>])"/, '\1”' ], # double closing + [ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing + [ /"/, '“' ], # double opening + [ /\b( )?\.{3}/, '\1…' ], # ellipsis + [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym + [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^\2\3', :no_span_caps ], # 3+ uppercase caps + [ /(\.\s)?\s?--\s?/, '\1—' ], # em dash + [ /\s->\s/, ' → ' ], # right arrow + [ /\s-\s/, ' – ' ], # en dash + [ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign + [ /\b ?[(\[]TM[\])]/i, '™' ], # trademark + [ /\b ?[(\[]R[\])]/i, '®' ], # registered + [ /\b ?[(\[]C[\])]/i, '©' ] # copyright + ] + + H_ALGN_VALS = { + '<' => 'left', + '=' => 'center', + '>' => 'right', + '<>' => 'justify' + } + + V_ALGN_VALS = { + '^' => 'top', + '-' => 'middle', + '~' => 'bottom' + } + + QTAGS = [ + ['**', 'b'], + ['*', 'strong'], + ['??', 'cite', :limit], + ['-', 'del', :limit], + ['__', 'i'], + ['_', 'em', :limit], + ['%', 'span', :limit], + ['+', 'ins', :limit], + ['^', 'sup'], + ['~', 'sub'] + ] + QTAGS.collect! do |rc, ht, rtype| + rcq = Regexp::quote rc + re = + case rtype + when :limit + /(\W) + (#{rcq}) + (#{C}) + (?::(\S+?))? + (.+?) + #{rcq} + (?=\W)/x + else + /(#{rcq}) + (#{C}) + (?::(\S+?))? + (.+?) + #{rcq}/xm + end + [rc, ht, re, rtype] + end + + # + # Flexible HTML escaping + # + def htmlesc( str, mode ) + str.gsub!( '&', '&' ) + str.gsub!( '"', '"' ) if mode != :NoQuotes + str.gsub!( "'", ''' ) if mode == :Quotes + str.gsub!( '<', '<') + str.gsub!( '>', '>') + end + + # Search and replace for Textile glyphs (quotes, dashes, other symbols) + def pgl( text ) + GLYPHS.each do |re, resub, tog| + next if tog and method( tog ).call + text.gsub! re, resub + end + end + + # Parses Textile attribute lists and builds an HTML attribute string + def pba( text_in, element = "" ) + + return '' unless text_in + + style = [] + text = text_in.dup + if element == 'td' + colspan = $1 if text =~ /\\(\d+)/ + rowspan = $1 if text =~ /\/(\d+)/ + style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN + end + + style << "#{ $1 };" if not filter_styles and + text.sub!( /\{([^}]*)\}/, '' ) + + lang = $1 if + text.sub!( /\[([^)]+?)\]/, '' ) + + cls = $1 if + text.sub!( /\(([^()]+?)\)/, '' ) + + style << "padding-left:#{ $1.length }em;" if + text.sub!( /([(]+)/, '' ) + + style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' ) + + style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN + + cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/ + + atts = '' + atts << " style=\"#{ style.join }\"" unless style.empty? + atts << " class=\"#{ cls }\"" unless cls.to_s.empty? + atts << " lang=\"#{ lang }\"" if lang + atts << " id=\"#{ id }\"" if id + atts << " colspan=\"#{ colspan }\"" if colspan + atts << " rowspan=\"#{ rowspan }\"" if rowspan + + atts + end + + TABLE_RE = /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m + + # Parses a Textile table block, building HTML from the result. + def block_textile_table( text ) + text.gsub!( TABLE_RE ) do |matches| + + tatts, fullrow = $~[1..2] + tatts = pba( tatts, 'table' ) + tatts = shelve( tatts ) if tatts + rows = [] + + fullrow. + split( /\|$/m ). + delete_if { |x| x.empty? }. + each do |row| + + ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m + + cells = [] + row.split( '|' ).each do |cell| + ctyp = 'd' + ctyp = 'h' if cell =~ /^_/ + + catts = '' + catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/ + + unless cell.strip.empty? + catts = shelve( catts ) if catts + cells << "\t\t\t#{ cell }" + end + end + ratts = shelve( ratts ) if ratts + rows << "\t\t\n#{ cells.join( "\n" ) }\n\t\t" + end + "\t\n#{ rows.join( "\n" ) }\n\t\n\n" + end + end + + LISTS_RE = /^([#*]+?#{C} .*?)$(?![^#*])/m + LISTS_CONTENT_RE = /^([#*]+)(#{A}#{C}) (.*)$/m + + # Parses Textile lists and generates HTML + def block_textile_lists( text ) + text.gsub!( LISTS_RE ) do |match| + lines = match.split( /\n/ ) + last_line = -1 + depth = [] + lines.each_with_index do |line, line_id| + if line =~ LISTS_CONTENT_RE + tl,atts,content = $~[1..3] + if depth.last + if depth.last.length > tl.length + (depth.length - 1).downto(0) do |i| + break if depth[i].length == tl.length + lines[line_id - 1] << "\n\t\n\t" + depth.pop + end + end + if depth.last.length == tl.length + lines[line_id - 1] << '' + end + end + unless depth.last == tl + depth << tl + atts = pba( atts ) + atts = shelve( atts ) if atts + lines[line_id] = "\t<#{ lT(tl) }l#{ atts }>\n\t
  • #{ content }" + else + lines[line_id] = "\t\t
  • #{ content }" + end + last_line = line_id + + else + last_line = line_id + end + if line_id - last_line > 1 or line_id == lines.length - 1 + depth.delete_if do |v| + lines[last_line] << "
  • \n\t" + end + end + end + lines.join( "\n" ) + end + end + + CODE_RE = /(\W) + @ + (?:\|(\w+?)\|)? + (.+?) + @ + (?=\W)/x + + def inline_textile_code( text ) + text.gsub!( CODE_RE ) do |m| + before,lang,code,after = $~[1..4] + lang = " lang=\"#{ lang }\"" if lang + rip_offtags( "#{ before }#{ code }#{ after }" ) + end + end + + def lT( text ) + text =~ /\#$/ ? 'o' : 'u' + end + + def hard_break( text ) + text.gsub!( /(.)\n(?! *[#*\s|]|$)/, "\\1
    " ) if hard_breaks + end + + BLOCKS_GROUP_RE = /\n{2,}(?! )/m + + def blocks( text, deep_code = false ) + text.replace( text.split( BLOCKS_GROUP_RE ).collect do |blk| + plain = blk !~ /\A[#*> ]/ + + # skip blocks that are complex HTML + if blk =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1 + blk + else + # search for indentation levels + blk.strip! + if blk.empty? + blk + else + code_blk = nil + blk.gsub!( /((?:\n(?:\n^ +[^\n]*)+)+)/m ) do |iblk| + flush_left iblk + blocks iblk, plain + iblk.gsub( /^(\S)/, "\t\\1" ) + if plain + code_blk = iblk; "" + else + iblk + end + end + + block_applied = 0 + @rules.each do |rule_name| + block_applied += 1 if ( rule_name.to_s.match /^block_/ and method( rule_name ).call( blk ) ) + end + if block_applied.zero? + if deep_code + blk = "\t
    #{ blk }
    " + else + blk = "\t

    #{ blk }

    " + end + end + # hard_break blk + blk + "\n#{ code_blk }" + end + end + + end.join( "\n\n" ) ) + end + + def textile_bq( tag, atts, cite, content ) + cite, cite_title = check_refs( cite ) + cite = " cite=\"#{ cite }\"" if cite + atts = shelve( atts ) if atts + "\t\n\t\t#{ content }

    \n\t" + end + + def textile_p( tag, atts, cite, content ) + atts = shelve( atts ) if atts + "\t<#{ tag }#{ atts }>#{ content }" + end + + alias textile_h1 textile_p + alias textile_h2 textile_p + alias textile_h3 textile_p + alias textile_h4 textile_p + alias textile_h5 textile_p + alias textile_h6 textile_p + + def textile_fn_( tag, num, atts, cite, content ) + atts << " id=\"fn#{ num }\"" + content = "#{ num } #{ content }" + atts = shelve( atts ) if atts + "\t#{ content }

    " + end + + BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m + + def block_textile_prefix( text ) + if text =~ BLOCK_RE + tag,tagpre,num,atts,cite,content = $~[1..6] + atts = pba( atts ) + + # pass to prefix handler + if respond_to? "textile_#{ tag }", true + text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) ) + elsif respond_to? "textile_#{ tagpre }_", true + text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) ) + end + end + end + + SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m + def block_markdown_setext( text ) + if text =~ SETEXT_RE + tag = if $2 == "="; "h1"; else; "h2"; end + blk, cont = "<#{ tag }>#{ $1 }", $' + blocks cont + text.replace( blk + cont ) + end + end + + ATX_RE = /\A(\#{1,6}) # $1 = string of #'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #'s (not counted) + $/x + def block_markdown_atx( text ) + if text =~ ATX_RE + tag = "h#{ $1.length }" + blk, cont = "<#{ tag }>#{ $2 }\n\n", $' + blocks cont + text.replace( blk + cont ) + end + end + + MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m + + def block_markdown_bq( text ) + text.gsub!( MARKDOWN_BQ_RE ) do |blk| + blk.gsub!( /^ *> ?/, '' ) + flush_left blk + blocks blk + blk.gsub!( /^(\S)/, "\t\\1" ) + "
    \n#{ blk }\n
    \n\n" + end + end + + MARKDOWN_RULE_RE = /^#{ + ['*', '-', '_'].collect { |ch| '( ?' + Regexp::quote( ch ) + ' ?){3,}' }.join( '|' ) + }$/ + + def block_markdown_rule( text ) + text.gsub!( MARKDOWN_RULE_RE ) do |blk| + "
    " + end + end + + # XXX TODO XXX + def block_markdown_lists( text ) + end + + def inline_markdown_link( text ) + end + + def inline_textile_span( text ) + QTAGS.each do |qtag_rc, ht, qtag_re, rtype| + text.gsub!( qtag_re ) do |m| + + case rtype + when :limit + sta,qtag,atts,cite,content = $~[1..5] + else + qtag,atts,cite,content = $~[1..4] + sta = '' + end + atts = pba( atts ) + atts << " cite=\"#{ cite }\"" if cite + atts = shelve( atts ) if atts + + "#{ sta }<#{ ht }#{ atts }>#{ content }" + + end + end + end + + LINK_RE = / + ([\s\[{(]|[#{PUNCT}])? # $pre + " # start + (#{C}) # $atts + ([^"]+?) # $text + \s? + (?:\(([^)]+?)\)(?="))? # $title + ": + (\S+?) # $url + (\/)? # $slash + ([^\w\/;]*?) # $post + (?=<|\s|$) + /x + + def inline_textile_link( text ) + text.gsub!( LINK_RE ) do |m| + pre,atts,text,title,url,slash,post = $~[1..7] + + url, url_title = check_refs( url ) + title ||= url_title + + atts = pba( atts ) + atts = " href=\"#{ url }#{ slash }\"#{ atts }" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) if atts + + "#{ pre }#{ text }#{ post }" + end + end + + MARKDOWN_REFLINK_RE = / + \[([^\[\]]+)\] # $text + [ ]? # opt. space + (?:\n[ ]*)? # one optional newline followed by spaces + \[(.*?)\] # $id + /x + + def inline_markdown_reflink( text ) + text.gsub!( MARKDOWN_REFLINK_RE ) do |m| + text, id = $~[1..2] + + if id.empty? + url, title = check_refs( text ) + else + url, title = check_refs( id ) + end + + atts = " href=\"#{ url }\"" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) + + "#{ text }" + end + end + + MARKDOWN_LINK_RE = / + \[([^\[\]]+)\] # $text + \( # open paren + [ \t]* # opt space + ? # $href + [ \t]* # opt space + (?: # whole title + (['"]) # $quote + (.*?) # $title + \3 # matching quote + )? # title is optional + \) + /x + + def inline_markdown_link( text ) + text.gsub!( MARKDOWN_LINK_RE ) do |m| + text, url, quote, title = $~[1..4] + + atts = " href=\"#{ url }\"" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) + + "#{ text }" + end + end + + TEXTILE_REFS_RE = /(^ *)\[([^\n]+?)\](#{HYPERLINK})(?=\s|$)/ + MARKDOWN_REFS_RE = /(^ *)\[([^\n]+?)\]:\s+?(?:\s+"((?:[^"]|\\")+)")?(?=\s|$)/m + + def refs( text ) + @rules.each do |rule_name| + method( rule_name ).call( text ) if rule_name.to_s.match /^refs_/ + end + end + + def refs_textile( text ) + text.gsub!( TEXTILE_REFS_RE ) do |m| + flag, url = $~[2..3] + @urlrefs[flag.downcase] = [url, nil] + nil + end + end + + def refs_markdown( text ) + text.gsub!( MARKDOWN_REFS_RE ) do |m| + flag, url = $~[2..3] + title = $~[6] + @urlrefs[flag.downcase] = [url, title] + nil + end + end + + def check_refs( text ) + ret = @urlrefs[text.downcase] if text + ret || [text, nil] + end + + IMAGE_RE = / + (

    |.|^) # start of line? + \! # opening + (\<|\=|\>)? # optional alignment atts + (#{C}) # optional style,class atts + (?:\. )? # optional dot-space + ([^\s(!]+?) # presume this is the src + \s? # optional space + (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title + \! # closing + (?::#{ HYPERLINK })? # optional href + /x + + def inline_textile_image( text ) + text.gsub!( IMAGE_RE ) do |m| + stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8] + atts = pba( atts ) + atts = " src=\"#{ url }\"#{ atts }" + atts << " title=\"#{ title }\"" if title + atts << " alt=\"#{ title }\"" + # size = @getimagesize($url); + # if($size) $atts.= " $size[3]"; + + href, alt_title = check_refs( href ) if href + url, url_title = check_refs( url ) + + out = '' + out << "" if href + out << "" + out << "#{ href_a1 }#{ href_a2 }" if href + + if algn + algn = h_align( algn ) + if stln == "

    " + out = "

    #{ out }" + else + out = "#{ stln }

    #{ out }
    " + end + else + out = stln + out + end + + out + end + end + + def shelve( val ) + @shelf << val + " <#{ @shelf.length }>" + end + + def retrieve( text ) + @shelf.each_with_index do |r, i| + text.gsub!( " <#{ i + 1 }>", r ) + end + end + + def incoming_entities( text ) + ## turn any incoming ampersands into a dummy character for now. + ## This uses a negative lookahead for alphanumerics followed by a semicolon, + ## implying an incoming html entity, to be skipped + + text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" ) + end + + def no_textile( text ) + text.gsub!( /(^|\s)==([^=]+.*?)==(\s|$)?/, + '\1\2\3' ) + text.gsub!( /^ *==([^=]+.*?)==/m, + '\1\2\3' ) + end + + def clean_white_space( text ) + # normalize line breaks + text.gsub!( /\r\n/, "\n" ) + text.gsub!( /\r/, "\n" ) + text.gsub!( /\t/, ' ' ) + text.gsub!( /^ +$/, '' ) + text.gsub!( /\n{3,}/, "\n\n" ) + text.gsub!( /"$/, "\" " ) + + # if entire document is indented, flush + # to the left side + flush_left text + end + + def flush_left( text ) + indt = 0 + if text =~ /^ / + while text !~ /^ {#{indt}}\S/ + indt += 1 + end unless text.empty? + if indt.nonzero? + text.gsub!( /^ {#{indt}}/, '' ) + end + end + end + + def footnote_ref( text ) + text.gsub!( /\b\[([0-9]+?)\](\s)?/, + '\1\2' ) + end + + OFFTAGS = /(code|pre|kbd|notextile)/ + OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi + OFFTAG_OPEN = /<#{ OFFTAGS }/ + OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/ + HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m + ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m + + def inline_textile_glyphs( text, level = 0 ) + if text !~ HASTAG_MATCH + pgl text + footnote_ref text + else + codepre = 0 + text.gsub!( ALLTAG_MATCH ) do |line| + ## matches are off if we're between ,
     etc.
    +                if $1
    +                    if line =~ OFFTAG_OPEN
    +                        codepre += 1
    +                    elsif line =~ OFFTAG_CLOSE
    +                        codepre -= 1
    +                        codepre = 0 if codepre < 0
    +                    end 
    +                elsif codepre.zero?
    +                    inline_textile_glyphs( line, level + 1 )
    +                else
    +                    htmlesc( line, :NoQuotes )
    +                end
    +                ## p [level, codepre, orig_line, line]
    +
    +                line
    +            end
    +        end
    +    end
    +
    +    def rip_offtags( text )
    +        if text =~ /<.*>/
    +            ## strip and encode 
     content
    +            codepre, used_offtags = 0, {}
    +            text.gsub!( OFFTAG_MATCH ) do |line|
    +                if $3
    +                    offtag, aftertag = $4, $5
    +                    codepre += 1
    +                    used_offtags[offtag] = true
    +                    if codepre - used_offtags.length > 0
    +                        htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
    +                        @pre_list.last << line
    +                        line = ""
    +                    else
    +                        htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
    +                        line = ""
    +                        @pre_list << "#{ $3 }#{ aftertag }"
    +                    end
    +                elsif $1 and codepre > 0
    +                    if codepre - used_offtags.length > 0
    +                        htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
    +                        @pre_list.last << line
    +                        line = ""
    +                    end
    +                    codepre -= 1 unless codepre.zero?
    +                    used_offtags = {} if codepre.zero?
    +                end 
    +                line
    +            end
    +        end
    +        text
    +    end
    +
    +    def smooth_offtags( text )
    +        unless @pre_list.empty?
    +            ## replace 
     content
    +            text.gsub!( // ) { @pre_list[$1.to_i] }
    +        end
    +    end
    +
    +    def inline( text ) 
    +        @rules.each do |rule_name|
    +            method( rule_name ).call( text ) if rule_name.to_s.match /^inline_/
    +        end
    +    end
    +
    +    def h_align( text ) 
    +        H_ALGN_VALS[text]
    +    end
    +
    +    def v_align( text ) 
    +        V_ALGN_VALS[text]
    +    end
    +
    +    def textile_popup_help( name, windowW, windowH )
    +        ' ' + name + '
    ' + end + + # HTML cleansing stuff + BASIC_TAGS = { + 'a' => ['href', 'title'], + 'img' => ['src', 'alt', 'title'], + 'br' => [], + 'i' => nil, + 'u' => nil, + 'b' => nil, + 'pre' => nil, + 'kbd' => nil, + 'code' => ['lang'], + 'cite' => nil, + 'strong' => nil, + 'em' => nil, + 'ins' => nil, + 'sup' => nil, + 'sub' => nil, + 'del' => nil, + 'table' => nil, + 'tr' => nil, + 'td' => ['colspan', 'rowspan'], + 'th' => nil, + 'ol' => nil, + 'ul' => nil, + 'li' => nil, + 'p' => nil, + 'h1' => nil, + 'h2' => nil, + 'h3' => nil, + 'h4' => nil, + 'h5' => nil, + 'h6' => nil, + 'blockquote' => ['cite'] + } + + def clean_html( text, tags = BASIC_TAGS ) + text.gsub!( /]*)>/ ) do + raw = $~ + tag = raw[2].downcase + if tags.has_key? tag + pcs = [tag] + tags[tag].each do |prop| + ['"', "'", ''].each do |q| + q2 = ( q != '' ? q : '\s' ) + if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i + attrv = $1 + next if prop == 'src' and attrv !~ /^http/ + pcs << "#{prop}=\"#{$1.gsub('"', '\\"')}\"" + break + end + end + end if tags[tag] + "<#{raw[1]}#{pcs.join " "}>" + else + " " + end + end + end +end + diff --git a/extensions/extexception.php b/extensions/extexception.php new file mode 100755 index 0000000..c1156fd --- /dev/null +++ b/extensions/extexception.php @@ -0,0 +1,53 @@ + + // @created 2005-10-07 + // @desc An Exception class specifically for 3rd party extensions (to be extended) + + class ExtException extends Exception { + // private variables + public $nErrorCode; + public $strErrorMessage; + public $strErrorLocation; + public $strErrorContext; + public $aDump; + + // constructor + public function __construct($nErrorCode = "0099", $strErrorMessage = "Undefined", $strErrorLocation = "", $strErrorContext = "", $aDump = "") { + $this->nErrorCode = $nErrorCode; + $this->strErrorMessage = $strErrorMessage; + $this->strErrorLocation = $strErrorLocation; + $this->strErrorContext = $strErrorContext; + $this->aDump = $aDump; + } + + // functions + public function GetErrorCode() { + return $this->nErrorCode; + } + public function GetErrorMessage() { + return $this->strErrorMessage; + } + public function GetErrorLocation() { + return $this->strErrorLocation; + } + public function GetErrorContext() { + return $this->strErrorContext; + } + public function GetDump() { + return $this->aDump; + } + public function AsString($bHTML = false) { + if($bHTML) { + return "Error No. {$this->strErrorCode}: {$this->strErrorMessage} ({$this->strErrorLocation})"; + } else { + return "Error No. {$this->strErrorCode}: {$this->strErrorMessage} ({$this->strErrorLocation})"; + } + } + + // aliases + public function GetError() { + return "Error No. " . $this->GetErrorCode() . ": " . $this->GetErrorMessage(); + } + } +?> \ No newline at end of file diff --git a/helpers/application_helper.php b/helpers/application_helper.php new file mode 100644 index 0000000..bcc7524 --- /dev/null +++ b/helpers/application_helper.php @@ -0,0 +1,254 @@ +protocol, $request->host, $request->directory, $resource); + } + + // link_to + public static function link_to($params, &$smarty) { + // get location + $location = $smarty->_tpl_vars['request']->location; + + // ancient stuff, for review (pull out the sanitize stuff) + // $controller = !empty($params["controller"]) ? $params["controller"] : $smarty->_tpl_vars['request']->route['controller']; + // $action = !empty($params["action"]) ? $params["action"] : $smarty->_tpl_vars['request']->route['action']; + // $id = !empty($params["id"]) ? self::sanitize_id($params["id"]) : ""; + // $mul = !empty($params["mul"]) ? $params["mul"] : ""; + // $mul_id = !empty($params["mulid"]) ? self::sanitize_id($params["mulid"]) : ""; + // if(!empty($mul)) $id = $id . $mul; + // if(!empty($mul_id)) $id = $id . $mul_id; + + // assemble link + $rel = !empty($params['rel']) ? " rel='{$params['rel']}'" : ""; + $html_options = $params["extra"]; // change from 'extra' to 'html_options' (will affect template code) + $link_item = $params["title"]; + + $onclick = !empty($params["confirm"]) ? sprintf(' onClick="if(confirm(\'%s\')) return true; else return false;"', $params["confirm"]) : ""; + $onclick = !empty($params["onclick"]) ? sprintf(' onClick="%s"', $params["onclick"]) : $onclick; + + if(empty($link_item) && !empty($params['image'])) $link_item = self::img($params, $smarty); + // if(empty($link_item)) $link_item = $url; + + // remove non-route params + unset($params['rel']); + unset($params['extra']); + unset($params['title']); + unset($params['confirm']); + unset($params['onclick']); + + // assemble route + $route = Router2::url_for($params); + + // conjoin location and route + $url = empty($params['url']) ? "{$location}{$route}" : $params['url']; + + $link = '%s'; + + return sprintf($link, $url, $html_options, $rel, $onclick, $link_item); + } + + // creates an image + public static function img($params, &$smarty) { + $location = $smarty->_tpl_vars['request']->location; + $img_dir = empty($params['img_dir']) ? "res/" : $params['img_dir']; + $image = $params['image']; + $border = empty($params['img_border']) ? "0" : $params['img_border']; + $alt_text = empty($params['img_alt']) ? "" : $params['img_alt']; + $extra = $params['img_extra']; + + // string template + $img = '%s'; + + // return image tag + return sprintf($img, $location, $img_dir, $image, $border, $alt_text, $extra); + } + + // create_link + public static function create_link($params, &$smarty) { + return self::url_for($params, $smarty); + } + public static function url_for($params, &$smarty) { + // get location + $location = $smarty->_tpl_vars['request']->location; + + // assemble route + $route = Router2::url_for($params); + + // old school (glean the default values from it) + // $controller = !empty($params["controller"]) ? $params["controller"] : $smarty->_tpl_vars['request']->route['controller']; + // $action = !empty($params["action"]) ? $params["action"] : $smarty->_tpl_vars['request']->route['action']; + // $id = !empty($params["id"]) ? self::sanitize_id($params["id"]) : ""; + + $link = '%s%s'; + + return sprintf($link, $location, $route); + } + + // sanitizes IDs (for IDs like Tags or Categories that can be separated by spaces...) + public static function sanitize_id($id) { + return urlencode($id); + } + + // select + public static function select($params, &$smarty) { + // @params name, options, selected, html_options + // set variables + $name = $params["name"]; + $select_options = $params["options"]; + $selected = $params["selected"]; + $html_options = $params["extra"]; // change from 'extra' to something else considering there are already 'options' + + // templates + $select_template = ''; + $option_template = ''; + + foreach($select_options as $value=>$option) { + $option_selected = ($selected == $value) ? 'selected="selected"' : ''; + $options .= sprintf($option_template, $value, $option_selected, $option); + } + + return sprintf($select_template, $name, $html_options, $options); + } + + public static function textile($params, &$smarty) { + return RedCloth::to_html($params['text']); + } + + // format_post + public static function format_post($params, &$smarty) { + $output = RedCloth::to_html($params['post']); + return $output; + + $post = stripslashes($params["post"]); + + $p = '

    %s

    '; + + foreach(explode("\n\n", str_replace("\r", "", $post)) as $para) { + if(empty($para)) continue; + + $paras .= sprintf($p, $para) . "\n"; + + if($params['truncate'] == 'intro') break; // just have the first paragraph (ingenious, no?) + } + $post = $paras; + + // do specialized formatting (Textile style) + + return $post; + } + + // format_comment + public static function format_comment($params, &$smarty) { + $comment = htmlentities(stripslashes($params["comment"])); + + $p = '

    %s

    '; + foreach(explode("\n\n", str_replace("\r", "", $comment)) as $para) { + $paras .= sprintf($p, $para) . "\n"; + } + $comment = $paras; + + // do specialized formatting (Textile style) + + return $comment; + } + + // count_comments + public static function count_comments($params, &$smarty) { + return count($params["comments"]); + } + + // pluralize + public static function pluralize($params, &$smarty) { + $word = $params['word']; + $number = (is_array($params['number'])) ? count($params['number']) : intval($params['number']); + $string_together = ($params['string_together'] == 'true') ? true : false; + + // pluralize word if the count is greater than 1 + if($number > 1 || $number == 0) $word = Pluralize::pluralize($word); + + // Pluralize supports alternate syntax: + // - $Pluralize::word // returns 'words' regardless of number + // - $Pluralize::word = number // returns 'word' or 'words' depending on number given to it + // note: this is only supported for instances and not static syntax + + // if you want to have the string "12 comments" or "1 feast" returned instead of just the pluralized form + if($string_together) $word = sprintf("%d %s", $number, $word); + + return $word; + + // @refer_to "An Algorithmic Approach to English Pluralization":http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html + } + + // date functions + public $date_formats = array( + 'standard'=>'Y-m-d', + 'standard+time'=>'Y-m-d H:i:s', + 'natural'=>'F j, Y, g:i a', + 'mysql'=>'Y-m-d H:i:s', + ); + public static function now($params, &$smarty) { + if(!empty(self::$date_formats[$params['format']])) $format = self::$date_formats[$params['format']]; + if(empty($params['format'])) $format = "Y-m-d H:i:s"; else $format = $params['format']; + return date($format); + } + + // filters (make sure these are registered in $treat_as_filter at the top) + public static function ago($date) { + $date = $date . ' ' . date('H:i:s'); + $date = strtotime($date); + $days_ago = (strftime("%j") + strftime("%Y") * 365) - (strftime("%j", $date) + strftime("%Y", $date) * 365); + if($days_ago < 1) { + $hours_ago = (strftime("%H") - strftime("%H", $date)); + if($hours_ago == 0) return "under a minute ago"; + if($hours_ago < 1) return "under an hour ago"; + if($hours_ago == 1) return "about an hour ago"; + return "today"; + } else if($days_ago < 7) { + // handle under a week + if($days_ago == 1) return "yesterday"; + return "about {$days_ago} days ago"; + } else if($days_ago < 27) { + // handle weeks + $weeks_ago = round($days_ago/7); + if($weeks_ago == 1) return "about a week ago"; + return "about {$weeks_ago} weeks ago"; + } else if($days_ago < 360) { + // handle months + $months_ago = round($days_ago/30); + if($months_ago == 1) return "about a month ago"; + return "about {$months_ago} months ago"; + } else { + // handle years + $years_ago = round($days_ago/366); + if($years_ago == 1) return "about a year ago"; + return "about {$years_ago} years ago"; + } + } + public static function human_readable($filesize) { + if(($filesize/1024) < 1024 ) + return round(($filesize/1024), 2) . 'kb'; // return filesize in KB + else + return round((($filesize/1024)/1024), 2) . 'mb'; // otherwise, filesize in MB + } + public static function size_by_popularity($tag, $full_usage = 50, $smallest = 6) { + // popularity, uses, total + return (string)(round((float)$tag->popularity) + 0.8);// smaller ems + return (string)(round((float)$tag->popularity + 0.8));// smaller ems + return (string)(round((float)$tag->popularity) + 1);// ems + return (string)((((float)$tag->popularity * 2) + 1) * $smallest); // weighted + return (string)(((float)$tag->popularity + 1) * $smallest); // simple trajectory + return (string)((int)($full_usage - ($full_usage / (((float)$tag->popularity) / 1.8)))); // old + } + public static function inspect($object) { + print "
    ";
    +			print_r($object);
    +			print "
    "; + } + } +?> \ No newline at end of file diff --git a/library/AutoLoad.php b/library/AutoLoad.php new file mode 100755 index 0000000..55d7cf7 --- /dev/null +++ b/library/AutoLoad.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/library/Canvas.php b/library/Canvas.php new file mode 100755 index 0000000..2454fbc --- /dev/null +++ b/library/Canvas.php @@ -0,0 +1,27 @@ + + // @created 2006-03-29 + // @desc The core class every major Canvas component inherets from + // @requires stdexception.php (StdException class) + // @note Yes, it came a little late in the game. + + include_once('stdexception.php'); + + // classes + class Canvas { + protected $config = array(); + + // loads the configuration for the current component if no other config data is requested + protected function load_config($config_section = null) { + $config = new Config2(); // load the configuration + + // determine section, if any + if($config_section == true && !empty($this->config_section)) $config_section = $this->config_section; // if there's a special section name already set, use that + if($config_section == true) $config_section = strtolower(get_class()); // if nothing's been set, use the class name + + // set config data + $this->config = ($config_section == null) ? $config->config : $config->$config_section; + } + } +?> \ No newline at end of file diff --git a/library/Config2.php b/library/Config2.php new file mode 100755 index 0000000..6f26499 --- /dev/null +++ b/library/Config2.php @@ -0,0 +1,108 @@ +$dir) { + Conventions::$directories[$convention] = $dir; + } + + // map others + } + } + +?> \ No newline at end of file diff --git a/library/Controller.php b/library/Controller.php new file mode 100644 index 0000000..e8d3dd2 --- /dev/null +++ b/library/Controller.php @@ -0,0 +1,299 @@ +request = $request; + $this->session = new Session(); + $this->config = $this->load_config(); + $this->response = new Response(); + foreach($this->request->route->mapping() as $routing_symbol) { + $this->$routing_symbol = $this->request->route->$routing_symbol; + } + $this->environment = Config2::$environment; + } + + // dispatcher + /* @note If the user defined 'dispatch' as a controller action, it could + be disasterous. Instead, make it final so that it is not able + to be overridden. + @consider Consider changing this from 'dispatch' to '_dispatch' or even + '__dispatch', to simulate the 'magic methods' for classes, + like '__get' and '__call'. + Consider handling action response (for view rendering) in a + different way. + @summary Dispatcher handles the majority of the overhead logic, such as + making sure the appropriate instance variables are set correctly, + executing the default handles/events/callbacks, and actually + calling the requested action's function (transparently). + Currently, the response of the $action is put in the $response + instance variable, within the array index of ['response']. + */ + final public function dispatch($action, $id = null, $request = null) { + if(empty($action)) $action = "index"; + $this->action = $action; + $this->id = $id; + + // execute authentication & session handling event + $this->authenticate_request(); + $this->handle_sessions(); + + $this->before_action(); // callback + + try { + // dispatch action, calling the exception handler if an exception + // which is thrown is not caught + // $this->$action($id); // deprecated because it only supports one single param + call_user_func(array($this, $action), $this->request->params()); + } catch(Exception $e) { + $this->handle_exception($e); + } + + if($this->after_action_performed != true) { + $this->after_action(); // callback (if not already performed) + $this->after_action_performed = true; + } + + // render (if not already rendered; also, consider layouts in views) + if(!$this->rendered) $this->render(); + + return $this->response; + } + + // events + protected function authenticate_request() { + try { // check if the action is to be authenticated... skip it if it isn't + if(is_array($this->skip_authentication) && !empty($this->skip_authentication) && !in_array($this->action, $this->skip_authentication)) { + return $this->authenticate(); + } elseif(is_array($this->authenticate) && !empty($this->authenticate) && in_array($this->action, $this->authenticate)) { + return $this->authenticate(); + } else { + // dep // $this->redirect_to(array("controller"=>"admin")); // handled within 'authenticate()' + } + } catch(Exception $e) { + $this->handle_exception($e); + } + } + protected function authenticate() { + // this function is to be overwritten only if authentication is deemed necessary + return true; + } + + protected function handle_exception($e) { + $exception_type = get_class($e); // will be the type of exception that was thrown + $handler = !empty($this->exception_handlers[$exception_type]) ? $this->exception_handlers[$exception_type] : $this->exception_handlers['*']; + if(!empty($handler)) { + $this->$handler($e); + } else { + Debug::generic_exception_handler($e); + } + + // terminate execution on exception after handling exception + die(); + } + + protected function handle_sessions() { + // this function is to be overwritten only if specific session control is deemed necessary (rare!) + // this will require updating sessions and everything, particularly for flashes + } + + public function __call($name, $params) { + // @consider Consider handling dynamic requests. + + // catch-all function (throw an error) + throw new Exception("The {$this->request->action} action does not exist"); + } + + // renderers + protected function render($params = null) { + // if the after_action() hasn't been performed yet (by the action explicitly calling the render() method), call it now + if($this->after_action_performed != true) { + $this->after_action(); + $this->after_action_performed = true; + } + + $this->rendered = true; // so that multiple render calls aren't made (such as when the user calls render() and the dispatcher knows not to call render() too) + + $this->before_render(); // callback + + // so that if no $params were specified, it would still be an array and an access violation + // wouldn't occur below when $params['foo'] was accessed + if($params == null) $params = array(); + + // render results to screen + // passes important data to templates + // maybe just creates the View object and does these things? yeah + // or, even better, create Response and send it, from the main dispatcher, to a view object? + + // actually, respond with the template file + // also should provide the a 'url' and the basics of the header/caller information as properties of the 'response' object/[array] + $this->response->layout = (!empty($params['layout'])) ? "{$params[layout]}.php" : (file_exists("views/{$this->controller}/layout.php") ? "layout.php" : '../layout.php'); + if($params['layout'] == "null") $this->response->layout = null; + if(empty($this->response->template)) $this->response->template = (!empty($params['template'])) ? "{$params[template]}.php" : "{$this->action}.php"; // for smarty, took out 'views/{$this->controller}/' before because smarty keeps the template dir internally + $this->response->url = "{$this->controller}/{$this->action}/{$this->id}"; + $this->response->controller = $this->controller; + $this->response->action = $this->action; + $this->response->id = $this->id; + $this->response->request = $this->request; + $this->response->flash = Session::flash(); + + View::render($this->response, $params); + + $this->after_render(); // callback + } + protected function render_partial($partial, $no_layout = true) { + $this->render(array("template"=>$partial, "layout"=>($no_layout ? "null" : null))); + } + protected function render_template($template, $no_layout = false) { + $this->render(array("template"=>$template, "layout"=>($no_layout ? "null" : null))); + } + protected function render_layout($layout) { + $this->render(array("layout"=>$layout)); + } + protected function redirect_to($request, $flash = null) { + $this->before_redirect(); // callback + + // $this->response->flash = Session::flash(); + $_SESSION['flash'] = $this->flash; + + // if there's continuance to be done in the future, put it in the session data + if(!empty($request['continue_to'])) { + $this->session->continue_to = $request['continue_to']; + $this->session->continue_to($request['continue_to']); + } + + // if the direct url hasn't been set, do normally... otherwise if it has been + if(empty($request['url'])) { + // redirect to the appropriate protocol if specified + if(!empty($request['protocol'])) $protocol = $request['protocol']; else $protocol = $this->request->protocol; + unset($request['protocol']); + + // location + $location = "{$protocol}://{$this->request->host}/{$this->request->directory}"; + + // assemble route + $route = Router2::url_for($request); + $url = '%s%s'; + $request = sprintf($url, $location, $route); + + // set defaults if not set + // if(empty($controller)) $controller = $this->controller; + // shouldn't happen... should forward to default action // if(empty($action)) $action = $this->action; // probably won't happen... doesn't make sense, does it? + // + // assemble request + // + // $request = "{$controller}/{$action}/{$id}"; + // if(empty($id)) $request = "{$controller}/{$action}"; + // if(empty($action)) $request = "{$controller}/"; + } else { + $request = $request['url']; + } + + // handle session closing to prevent data loss + Session::flush(); + + // actually redirect + header("Location: {$request}"); + + // not sure if this will ever happen, but you never know + $this->after_redirect(); // callback + + // log redirection + $execution_time = round(microtime(true) - $GLOBALS['dispatch_time_begin'], 5); + $execution_memory = round(memory_get_usage()/1024, 2); + Debug::log("Redirected {$_SERVER['PATH_INFO']} to {$path} ({$execution_time}sec/{$execution_memory}kb)", 'internal', 'notice', 'Controller'); + + // kill the rest of execution for this request + die("Action calls for redirection. Click here if you are not automatically forwarded."); + } + + // ajax response + protected function ajax_response($response) { + // log remote request + $execution_time = round(microtime(true) - $GLOBALS['dispatch_time_begin'], 5); + $execution_memory = round(memory_get_usage()/1024, 2); + Debug::log("Remote request {$_SERVER['PATH_INFO']} ({$execution_time}sec/{$execution_memory}kb)", 'internal', 'notice', 'Controller'); + + if(is_array($response)) { + // full on xml response + $this->response->response_id = $response['id']; + $this->response->units = $response['units']; + $this->render(array( + 'template'=>'../templates/ajax_response', + 'layout'=>'null', + )); + } else { + // plain text response + die($response); + } + } + + // property handler + // @desc These allow for developers to simply assign values to $this->foo + // and have them automatically sent to the Response object and, + // ultimately, the View object. + protected function __get($key) { + return $this->properties[$key]; + return $this->response->$key; + } + protected function __set($key, $value) { + return $this->properties[$key] = $value; + return $this->response->$key = $value; + } + + // flash function (which replaces just using $this->flash = array()) + protected function flash($message, $class, $params = array()) { + // graft the message and the class into optionally-specified the $params array (whereby more data can be passed on) + $params['message'] = $message; + $params['class'] = $class; + $this->flash = $params; + } + + // events/callbacks + protected function before_action() {} + protected function after_action() {} + protected function before_render() {} // @consider Consider moving this to the View class, or at least calling before_render() of the View as well... maybe + protected function after_render() {} + protected function before_redirect() {} + protected function after_redirect() {} // this is executed right before the header() call and the die() + + // descructor + public function __destruct() { + // possibly put some serialization code in here? + + $_SESSION['flash'] = $this->flash; + + session_write_close(); + + // end; + } + } +?> \ No newline at end of file diff --git a/library/Conventions.php b/library/Conventions.php new file mode 100755 index 0000000..290a4a5 --- /dev/null +++ b/library/Conventions.php @@ -0,0 +1,246 @@ + + // @created 2006-03-29 + // @desc Conventions and default values to be used throughout the system + // @requires stdexception.php (StdException class) + // @note Yes, it came a little late in the game. + + // This file is to be clean + + class Conventions { + public static $app_dir = ''; + + // path info (for routing) + public static function path_info() { + return substr($_SERVER['PATH_INFO'], 1); + } + + // 'directories' contains the directory names for various components. + // It will appear to be repetitive, but this is due to the fact that + // the keys are the same as the default so that the internal system + // never has to know it's something different! + public static $directories = array( + 'library'=>'library', // standard and concrete for now + 'adapters'=>'library/adapters', + 'controllers'=>'controllers', + 'models'=>'models', + 'helpers'=>'helpers', + 'views'=>'views', + 'config'=>'config', + 'logs'=>'logs', + 'extensions'=>'extensions', + ); // directories + + // static method to get the directory name + public static function directory($name) { + return self::$directories[$name]; + } + + // 'names' contains specifications for how names should be formed + // and derived. If custom functionality is desired, modify the + // names and the functions, such as to Camelize names for names + // as 'PublicController' instead of 'public_controller' + public static $names = array( + // config + 'config'=> array( + // cache file + 'cache'=> 'config.cache', + + // the generic file (for non-specific config-files) + 'file'=> '%s.yml', + + // explicit files + 'files'=> array( + 'routes'=> 'routes.php', + ), + ), + + // controllers + 'controllers'=> array( + 'class'=> '%s_controller', + 'file'=> '%s_controller.php', + 'action'=> '%s', + ), + + // models + 'models'=> array( + 'class'=> '%s', + 'file'=> '%s.php', + ), + + // views + 'views'=> array( + 'name'=> '%s', + 'class'=> '%s_view', + 'file'=> '%s_view.php', + 'action'=> '%s.php', + 'controller'=> '%s', + 'layout' => 'layout.php', + ), + + // helpers + 'helpers'=> array( + 'class'=> '%s_helper', + 'file'=> '%s_helper.php', + ), + + // libraries + 'library'=> array( + 'file'=> '%s.php', + 'adapter'=> '%s.php', + ), + + // extensions + 'extensions'=> array( + 'file'=> '%s.php', + ), + + // logs + 'logs'=> array( + 'file'=> '%s.log', + 'separate_file'=> '%s.%s.log' + ), + ); // names + + // naming methods /////////////////////////////////////////////////////////////////////////////////////////////////// + // form standard names + + // controller naming functions + public static function controller_name($name) { + return sprintf(self::$names['controllers']['class'], $name); + } + public static function controller_class_name($name) { + return self::controller_name($name); + } + public static function controller_file_name($name) { + return sprintf(self::$names['controllers']['file'], $name); + } + // action name + public static function action_name($name) { + return sprintf(self::$names['controllers']['action'], $name); + } + + // model naming functions + public static function model_name($name) { + return sprintf(self::$names['models']['class'], $name); + } + public static function model_class_name($name) { + return self::model_name($name); + } + public static function model_file_name($name) { + return sprintf(self::$names['models']['file'], $name); + } + + // view naming functions + public static function view_name($name) { + return sprintf(self::$names['views']['name'], $name); + } + public static function view_class_name($name) { + return sprintf(self::$names['views']['class'], $name); + } + public static function view_class_file_name($name) { + return sprintf(self::$names['views']['file'], $name); + } + public static function view_file_name($name) { + return sprintf(self::$names['views']['action'], $name); + } + public static function view_controller_name($name) { + return sprintf(self::$names['views']['controller'], $name); + } + public static function view_layout_file_name() { + return self::$names['views']['layout']; + } + + // helper naming functions + public static function helper_name($name) { + return sprintf(self::$names['helpers']['class'], $name); + } + public static function helper_class_name($name) { + return self::helper_name($name); + } + public static function helper_file_name($name) { + return sprintf(self::$names['helpers']['file'], $name); + } + + // extensions and libraries + public static function library_file_name($name) { + return sprintf(self::$names['library']['file'], $name); + } + public static function extension_file_name($name) { + return sprintf(self::$names['extensions']['file'], $name); + } + + // adapters + public static function adapter_file_name($name) { + return sprintf(self::$names['library']['adapter'], strtolower($name)); + } + + // paths methods ////////////////////////////////////////////////////////////////////////////////////////////////////// + + // generic path function + public static function path($dir, $file) { + return self::$app_dir . self::directory($dir) . '/' . $file; + } + + // controller path + public static function controller_path($name) { + return self::path('controllers', self::controller_file_name($name)); + } + // model path + public static function model_path($name) { + return self::path('models', self::model_file_name($name)); + } + // view path + public static function view_path($controller, $name) { + return self::path('views', (self::view_controller_name($controller) . '/' . self::view_file_name($name))); + } + public static function view_class_path($name) { + return self::path('views', self::view_class_name($name)); + } + public static function view_class_file_path($name) { + return self::path('views', self::view_class_file_name($name)); + } + // helper path + public static function helper_path($name) { + return self::path('helpers', self::helper_file_name($name)); + } + // library path + public static function library_path($name) { + return self::path('library', self::library_file_name($name)); + } + // extension path + public static function extension_path($name) { + return self::path('extensions', self::extension_file_name($name)); + } + // adapter path + public static function adapter_path($name) { + return self::path('adapters', self::adapter_file_name($name)); + } + + // specific file methods // + + // config cache file + public static function config_cache_file() { + return self::path('config', self::$names['config']['cache']); + } + // config file name + public static function config_file($name) { + // if the filename is explicitly set, return that value + if(!empty(self::$names['config']['files'][$name])) + return self::path('config', self::$names['config']['files'][$name]); + + // otherwise, return the default form of the config file (such as '{$name}.yml') + return self::path('config', sprintf(self::$names['config']['file'], $name)); + } + + // log file + // config file name + public static function log_file($name) { + return self::path('logs', sprintf(self::$names['logs']['file'], $name)); + } + public static function separate_log_file($section, $name) { + return self::path('logs', sprintf(self::$names['logs']['separate_file'], $section, $name)); + } + } +?> \ No newline at end of file diff --git a/library/Debug.php b/library/Debug.php new file mode 100755 index 0000000..f29b457 --- /dev/null +++ b/library/Debug.php @@ -0,0 +1,146 @@ + + // @created 2005-12-22 + // @desc Handles various aspects of debugging, such as logging, asserting, + // default exception handlers or verbose exception handlers, + // and potentially suppoting breakpoints/interactive + // inspection during runtime + // @requires stdexception.php (StdException class) + // @refer_to http://www.php.net/manual/en/function.proc-open.php for + // breakpoint/interactive inspection during runtime + // @refer_to http://www.php.net/manual/en/function.assert.php for + // assertions and peculiarities therein + // @refer_to http://www.php.net/manual/en/function.apd-breakpoint.php + + include_once('stdexception.php'); + + // classes + class Debug extends Canvas { + // property functions + public static function __set($name, $value) { + $GLOBALS["debug"][$name] = $value; + } + public static function __get($strName) { + return $GLOBALS["debug"][$name]; + } + + // functions + // Debug::log($message, $type = 'general', $level = 'general', $from = null); + public static function log($message, $type = 'general', $level = 'notice', $from = null) { + // handle logging the $message (possibly formatting) + + // configuration values + $env = Config2::$environment; + $log_config = Config2::$config['logging']; + $dir = Config2::$config['directories']['logs']; + $filename = "{$env}.log"; + if($filename == '.log') $filename = 'system.log'; + $log_level = $log_config['log_level']; + $log_separately = $log_config['log_separately']; // array of types of messages to log separetely (in their own logs) + $always_log = $log_config['always_log']; // array of types of messages to always log + + // handle default values (convention over configuration) + if(empty($dir)) $dir = 'logs/'; // default logging directory + if(empty($log_level)) $log_level = 'notice'; // default log level + if(empty($always_log)) $always_log = array(); + if(empty($log_separately)) $log_separately = array(); + // make sure logging is supposed to happen for this event + // if($env == 'production') return; // should logging be skipped in the production environment? + if(self::log_level($level) >= self::log_level($log_level) || in_array($type, $always_log) || in_array($type, $log_separately)) { + if(in_array($type, $log_separately)) $filename = "{$type}.log"; + } else { + if(!in_array($type, $always_log)) return; + } + + // log format + if(empty($log_config['log_format'])) + $log_format = "%s (%s:%s)%s %s\n"; + else + $log_format = $log_config['log_format']; + + // changes if it was set + if(!empty($from)) $from = " [{$from}]"; + + $log_file = new FSFile(FSFile::build_path($dir, $filename)); + $date = date("Y-m-d H:i:s"); // alternatively // date("c"); + $log_file->write(sprintf($log_format, $date, $type, $level, $from, $message)); + } + private static function log_level($level) { + // determines the log level value (a numerical index) for each logging level + // used to determine if logging of the specified level should be skipped or not + + // log levels and their numerical index + $log_levels = array( + "low"=>0, + "info"=>1, + "notice"=>2, + "warn"=>3, + "error"=>4, + "fatal"=>5, + ); + + // return numerical index + // return 0 if it's not in the array of predefined levels + return (array_key_exists($level, $log_levels)) ? $log_levels[$level] : 0; + } + + public static function assert($expression, $value) { + if($expression == $value) return true; // assertion was successful + else throw new AssertionException($expression); // assertion did not succeed and must be handled + + // if $expression is a boolean value, return it + // if not, evaluate it and then return that value + // if no value returned by expression, return null value + + // refer to http://www.php.net/manual/en/function.assert.php + } + + public static function breakpoint() { + // refer to http://www.php.net/manual/en/function.proc-open.php to possibly implement + // and also http://www.php.net/manual/en/function.apd-breakpoint.php + } + + // standard/verbose exception handlers + public static function generic_exception_handler($e) { + // handle generic exception + $exception_type = get_class($e); + $e_dump = print_r($e, true); + $template = << + + Exception: {$exception_type} + + +

    Internal Exception!

    +

    {$e->getMessage()}

    +
    {$e_dump}
    + + +TPL; + print $template; + die(); + } + public static function verbose_exception_handler($e) { + // verbosely evaluate the exception and return/print the evaluation for inspection during development + } + public static function minimal_exception_handler($e) { + // give as little error information as possible (reserved primarily for the production environment for unhandled exceptions, a bad thing to begin with) + } + + // handle timing + public static function timing($component) { + if(empty($GLOBALS['debug']['timing'][$component])) { + // begin timing + $GLOBALS['debug']['timing'][$component] = microtime(true); + } else { + // finish timing + $time = microtime(true) - $GLOBALS['debug']['timing'][$component]; + Debug::log("took $time seconds...", 'bebug:timing', 'warn', $component); + } + } + } + + class DebugException extends StdException {} // hopefully won't ever need to be used, but you never know + class AssertionException extends StdException {} // an assertion failure exception +?> \ No newline at end of file diff --git a/library/File.php b/library/File.php new file mode 100755 index 0000000..963ae5f --- /dev/null +++ b/library/File.php @@ -0,0 +1,154 @@ + + // @created 2006-02-06 + // @desc Handles interaction with the filesystem. + // @requires stdexception.php (StdException class) + // @refer_to http://www.php.net/manual/en/ref.filesystem.php + + include_once('stdexception.php'); + + // classes + class FSFile { + public $filename = null; + public $mode = "a"; // append by default (a+ for append and read) + public $file = null; + public $exists = false; + public $filesize = 0; + + // constructor + public function __construct($filename = null, $mode = null) { + if(!empty($filename)) $this->open($filename, $mode); + } + + // functions + public function open($filename = null, $mode = null) { + // make sure there's a filename to open + if($filename == null) $filename = $this->filename; + if($filename == null) return null; // return null if none present to open + // get reading/writing mode + if($mode == null) $mode = $this->mode; + + // if it doens't exist, return null + if(file_exists($filename)) $this->exists = true; else throw new FileDoesNotExistException(); // return null; // decided to throw an exception + + // get filesize + $this->filesize = filesize($filename); + + // open the file (returning the handle to $this->file) + $this->file = @fopen($filename, $mode); + if($this->file == null) throw new FileNotOpenException(); + } + + public function close() { + // close the file + return @fclose($this->file); + } + + // reading and writing functions + public function read() { + // check for existence and whether it's open + if(!$this->exists) return false; + if($this->file == null) $this->open(); + if($this->file == null) return false; + + // read and return everything + return fread($this->file, $this->filesize); + } + public function write($data) { + // check for existence and whether it's open + if(!$this->exists) return false; + if($this->file == null) $this->open(); + if($this->file == null) return false; + + // write to the file (following the guidelines of the writing mode) + $bytes_written = @fwrite($this->file, $data); + + // return true if the number of bytes written is greater than zero, or the same size as the source data + if(($bytes_written > 0) || (strlen($data) == $bytes_written)) + return true; + else + return false; + } + + // uploading functions + public static function upload($file, $destination, $id = null) { + if(!is_uploaded_file($file['tmp_name'])) return false; + + // destination + $destination = self::build_path($destination, self::sanitize_filename($file['name'], $id)); + + // if the file exists, throw an exception! + if(file_exists($destination)) throw new FileExistsAlreadyException("'{$destination}' already exists!"); + + if(move_uploaded_file($file['tmp_name'], $destination)) { + return true; + } else { + return false; // @consider maybe throw an exception? + } + } + + // file management functions + public static function delete($location, $filename) { + // get full filename + $filename = self::build_path($location, $filename); + + // check for existence + if(!file_exists($filename)) return false; + + // delete (aka unlink) file + if(!@unlink($filename)) return false; + return true; + } + public function remove($filename = null) { + // check for existence and whether it's open + if(!$this->exists) return false; + if($this->file == null) $this->open(); + if($this->file == null) return false; + + if(!@unlink($this->filename)) return false; + return true; + } + + // useful static functions + public static function sanitize_filename($filename, $id = null) { + // haha, 'sanitize' + $filename_info = pathinfo($filename); + $filename = str_replace(' ', '_', substr($filename_info['basename'], 0, -(strlen($filename_info['extension']) + ($filename_info['extension'] == '' ? 0 : 1)))); + return $filename . ($id ? ('_' . $id) : '') . ($filename_info['extension'] ? ".{$filename_info[extension]}" : ''); + } + + public static function current_dir() { + $working_dir = $GLOBALS['working_dir']; // set in Dispatcher // find a better way to do this! + return $working_dir . '/'; + } + + public static function build_path($dir, $filename = '') { + return self::current_dir() . $dir . '/' . $filename; + } + + // directory functions + public static function dir_listing($source_dir = null) { + if($source_dir == null) $source_dir = self::current_dir(); + $dir = dir($source_dir); + while (false !== ($file = $dir->read())) { + if($file != '.' && $file != '..' && !is_dir($source_dir . $file)) { + $files[] = $file; + } + } + + return $files; + } + + // descructor + public function __destruct() { + $this->close(); + } + } + + class FileException extends StdException { + // hopefully won't ever need to be used, but you never know + } + class FileNotOpenException extends StdException {} + class FileDoesNotExistException extends StdException {} +?> \ No newline at end of file diff --git a/library/Globals.php b/library/Globals.php new file mode 100755 index 0000000..fac17b2 --- /dev/null +++ b/library/Globals.php @@ -0,0 +1,45 @@ + + // @created 2005-09-26 + // @desc A session class for easily accessing and administering sessions (can + // transparently keep all session data in the database) + // @requires stdexception.php (StdException class) + + include_once('stdexception.php'); + + // constants + // hash type constants +// define("AUTH_HASHTYPE_SHA", 2); +// define("AUTH_HASHTYPE_MD5", 5); + + // classes + class Globals { + // static functions + public static function retreive($name) { + return $GLOBALS[$name]; + } + public static function store($values) { + foreach($values as $key=>$value) { + $GLOBALS[$key] = $value; + } + } + // special functions + public static function retreive_section($name, $section) { + return $GLOBALS[$name][$section]; + } + public static function store_section($section, $property, $value) { + $GLOBALS[$section][$property] = $value; + } + + // functions + public function __set($name, $value) { + $GLOBALS[$name] = $value; + } + public function __get($name) { + return $GLOBALS[$name]; + } + } + + class GlobalsException extends StdException {} +?> \ No newline at end of file diff --git a/library/LICENSE b/library/LICENSE new file mode 100644 index 0000000..c04d050 --- /dev/null +++ b/library/LICENSE @@ -0,0 +1,36 @@ +== Copyright + +Copyright© 2005-2006 Clayton State University[1]. + +== Usage + +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. + +== Warranty + +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. + +== Source + +The license for the Canvas web development framework is generously provided +by the Open Source Initiative[2], and is officially titled the MIT License[3]. + +== References + +[1] http://www.clayton.edu/ +[2] http://www.opensource.org/ +[3] http://www.opensource.org/licenses/mit-license.php \ No newline at end of file diff --git a/library/Model2.php b/library/Model2.php new file mode 100644 index 0000000..3c5bda5 --- /dev/null +++ b/library/Model2.php @@ -0,0 +1,457 @@ +database; + $adapter = strtolower($adapter[Config2::$environment]['adapter']); + + // load adapter + include_once Conventions::adapter_path($adapter); + + // classes + abstract class Model { + protected static $adapter = array(); // the current, active adapter + protected static $columns = array(); // columns for this table + public $table, $scope; // table name and scope (helps refine queries, usually for security purposes) + protected $rows = array(); // the row(s) being operated on or saught + protected $has_one, $has_many, // column associations + $has_property, $has_properties, + $belongs_to_many; + protected $validates; // data validation + + // associations default values + public $default_order = ''; + + // constructor and loader + public function __construct() { + // load configuration + $config = new Config2(); + + // get all database config data + $config = $config->database; + + // load environment-specific database connection information into the adapter + self::$adapter['config'] = $config[Config2::$environment]; + + // get the table name (usually the plural form of the model name, eg: shoe=>shoes or person=>people) + if(empty($this->table)) $this->table = Inflector::pluralize(get_class($this)); + + // make sure adapter is loaded + self::load_adapter(); + } + // __constructor static-aliases + public static function begin($model) { + return new $model(); + } + + protected static function load_adapter() { + // get the adapter + $adapter = strtolower(self::$adapter['config']['adapter']); + + // create an instance of the adapter if it doesn't already exist + if(empty(self::$adapter['handle'])) + self::$adapter['handle'] = new $adapter(self::$adapter['config']); + + // should this be here? do we want it to automatically connect? + self::$adapter['handle']->connect(); + self::$adapter['handle']->select_db(self::$adapter['config']['database']); + } + + protected function map_associations($params) { + if(array_search('non_greedy', $params) !== false) return $params['join']; + /* + @use + Internally, find() calls this to set up joins for any immediate + associations (has_one/has_propert(y/ies)) or greedy associations + (has_many) + */ + + // check for has_property + if(!empty($this->has_property)) + $this->has_properties[] = $this->has_property; + $this->has_property = null; + // and check for has_properties and then parse them + if(!empty($this->has_properties)) { + foreach(self::has_properties($this->has_properties) as $association) { + $joins[] = $association; + } + // $joins = array_unique($joins); // handle accidental duplication // deprecated of removing the data from $this->has_property instead + } + + // handle many-to-many relationships + if(!empty($this->belongs_to_many)) { + $joins[] = array('table'=>$this->belongs_to_many[0], 'on'=>':@table.:singular_table_id=:table.id'); + } + + /*// handle has_one relationships when they are specified as 'greedy' + if(false && !empty($this->has_one)) { + foreach(self::has_one($this->has_one) as $association) { + $joins[] = $association; + } + } + + // handle has_many relationships when they are specified as 'greedy' + if(false && !empty($this->has_many)) { + foreach(self::has_many($this->has_many as $association) { + $joins[] = $association; + } + }*/ + + // build up the joins into the proper format and return it + return self::build_joins_up($joins); + } + protected static function build_joins_up($all_joins) { + if(is_array($all_joins)) $joins = array_shift($all_joins); else return $all_joins; + if(!empty($all_joins)) $joins['join'] = self::build_joins_up($all_joins); + return $joins; + } + + // database action functions + public function find($params = null) { + $this->before_find(); // callback + + // reset results and iteration + $this->rows = null; + $this->rows = array(); + + // add the table to the parameters of the query and process all other automatic properties + if(empty($this->belongs_to_many)) { + $params['table'] = $this->table; + } else { // many-to-many relationship + $params['table'] = $this->belongs_to_many['through']; + } + // map associations + $params['join'] = $this->map_associations($params); + + // execute select + $this->rows = self::$adapter['handle']->find($params); // include $scope as third param to give even more definition to select queries (for security) + + if(!empty($this->rows)) foreach($this->rows as $id=>$row) { + if(empty($id) || empty($row)) continue; + $this->rows[$id] = $this->after_find_for_each($row); + } + + $this->after_find(); // callback + + // return $this to allow for stacked calls (like '$model->find()->all()') + return $this; + + // deprecated to keep from having to do tedious error checking when a find() is called... + // return $this when number of rows is greater than 0, false if 0 + // if(self::$adapter['handle']->rows_found() > 0) return $this; else return false; + } + + public function find_all($params = null) { + if(!$this->find($params)) return false; + return $this->all(); + } + + public function find_by($params) { + // $params will be an associative array of ids and values as well as the where clause (eg: 'id=:id') + if(!$this->find(array('where'=>$params))) return false; + return $this->all(); + } + + public function find_by_id($id) { + return $this->find(array("where"=>array(':table.id=":id"', 'id'=>$id))); + } + + public function save() { + $this->before_save(); // callback + + // set up the params + $params = array('table'=>$this->table, 'values'=>$this->remove_non_columns($this->current()), 'where'=>array('id=":id"', 'id'=>$this->id), 'limit'=>'1'); + + // save the current row and update the reference within the collection + if($row = self::$adapter['handle']->save($params)) { + if(key($this->rows) == 'new') unset($this->rows['new']); + $this->rows[$row->id] = $row; + } else throw new ModelException(); + + $this->after_save(); // callback + + // return $this when number of affected rows is greater than 0, false if 0 + if(self::$adapter['handle']->affected_rows() > 0) return $this; else return false; + } + + public function delete($params = null) { + $this->before_delete(); // callback + + if(!$this->id) return false; // either find() and then delete() or delete() with parameters! duh! + + // if $params is empty, give it a default selector of the current element + if(empty($params)) $params = array('table'=>$this->table, 'where'=>array('id=":id"', 'id'=>$this->id), 'limit'=>'1'); // delete the currently selected ID (and only the one) + + $result = self::$adapter['handle']->delete($params); + + // callback // passing in the parameters + // and the result/return value for good + // measure (for verbosity) + $this->after_delete($params, $result); + + // return $this when number of affected rows is greater than 0, false if 0 + if(self::$adapter['handle']->affected_rows() > 0) return $this; else return false; + } + public function delete_all($params = null) { + $this->before_delete(); // callback + + // perform a find if there aren't any results already + if($this->is_empty()) $this->find($params); + + // loop through results and delete away + foreach($this->rows as $rows) { + // delete this row + $this->delete(array('table'=>$this->table, 'where'=>array('id=":id"', 'id'=>$row->id), 'limit'=>'1')); + } + + $this->after_delete($params, $result); // callback + + // return true when number of affected rows is greater than 0, false if 0 + if($this->db->affected_rows() > 0) return true; else return false; + } + + // transactional functionality + public function start() { + // begin transaction + if($row = self::$adapter['handle']->begin()) { + // return on success + return true; + } else throw new ModelException(); + } + public function commit() { + // commit and end transaction + if($row = self::$adapter['handle']->commit()) { + // return on success + return true; + } else throw new ModelException(); + } + public function end() { + // alias of commit + return $this->commit(); + } + public function rollback() { + // rollback transactions if there were problems + if($row = self::$adapter['handle']->rollback()) { + // return on success + return true; + } else throw new ModelException(); + } + + // facilitates the 'find_by_blah_and_blah' dynamic find functions + public function __call($name, $params) { + if(substr_count($name, "find_by_") > 0) { // find by + // take out function name + $keys = str_replace("find_by_", "", $name); + + $params = $this->process_find_call($keys, $params); + + return $this->find(array("where"=>$params)); + } elseif(substr_count($name, "find_or_create_by_") > 0) { // find or create by + // take out function name + $keys = str_replace("find_or_create_by_", "", $name); + + $params = $this->process_find_call($keys, $params); + + if($this->find(array("where"=>$params))->is_empty()) { + foreach($params as $key=>$value) { + $this->$key = $value; + } + $this->save(); + } + + return true; + } else { + // something totally different and unexpected + } + } + protected function process_find_call($name, $func_params) { + // add support in for column1_or_column2 parsing + $columns = explode("_and_", $name); + while((list(, $key) = each($columns)) && (list(, $value) = each($func_params))) { + $params[$key] = $value; + } + return $params; + } + + public function __set($key, $value) { + if(empty($this->rows)) { + $this->rows['new'] = new row(); + $id = 'new'; + } else $id = ($this->current()->id) ? $this->current()->id : 'new'; + return $this->rows[$id]->$key = $value; + } + public function __get($key) { + if(empty($this->rows)) return false; + + $id = $this->current()->id; + + // handle associations if applicable + if($associate = $this->has_one($key)) { + $this->rows[$id]->$key = $associate; + return $associate; + } + if($associates = $this->has_many($key)) { + $this->rows[$id]->$key = $associates; + return $associates; + } + + // handle has_* requests (for tests if associations have been set or exist) + if(substr($key, 0, 4) == 'has_') { + $key = substr($key, 4); // get out the keyword (minus the has_ prefix) + $singular_key = Inflector::singularize(get_class($this)); + $count = $this->$key->count(array("{$singular_key}_id=':id'", 'id'=>$this->id)); + if(!empty($count)) return true; +// if(!empty($this->$key)) return true; +// if(!$this->$key->is_empty()) return true; + else return false; + } + + // handle quantity requests + if($key == 'count') return count($this->rows); + + // calls the current row object + // (important to remain this way because just doing $this->id would recursively call itself) + return stripslashes($this->rows[$this->current()->id]->$key); + } + + // iteration support methods + // returns the current row object (vital for __get() and __set() functionality) + public function current() { + if(empty($this->rows)) return false; + return current($this->rows); + } + // this returns $this->rows (an array of row objects) + public function all() { + if(empty($this->rows)) return false; + return $this->rows; + } + // the rest of these return $this to allow for $model->next()->id or such. + public function next() { + if(empty($this->rows)) return false; + next($this->rows); + return $this; + } + public function previous() { + if(empty($this->rows)) return false; + prev($this->rows); + return $this; + } + public function first() { + if(empty($this->rows)) return false; + reset($this->rows); + return $this; + } + public function last() { + if(empty($this->rows)) return false; + end($this->rows); + return $this; + } + + // get the data in an array form + public function all_as_array() { + if(empty($this->rows)) return false; + foreach($this->rows as $row) { + $rows[$row->id] = $row->as_array(); + } + return $rows; + } + + // check for the presence of data + public function is_empty() { + return empty($this->rows); + } + + // return number of entries + public function count($conditions = null) { + if($conditions === null) $conditions = array(); + $model = get_class($this); + $model = new $model(); + return $model->find(array('where'=>$conditions, 'columns'=>'count(id) as row_count', 'non_greedy'))->row_count; + } + public function count_rows() { + return count($this->rows); + } + + // verification and data integrity support methods + protected function remove_non_columns($row) { + // get columns (and cache them if they aren't cached already) + if(empty(self::$columns[$this->table])) self::$columns[$this->table] = self::$adapter['handle']->columns($this->table); + $row->remove_non_columns(self::$columns[$this->table]); + return $row; + } + + // assocation methods + protected static function has_properties($properties) { + // get {$with} + if($properties['with'] !== null) { + $with = $properties['with']; + unset($properties['with']); + } + + foreach($properties as $property) { + $property = explode('.', $property); + $table = $property[0]; + $columns = $property[1]; + $single_table_id = ((empty($with)) ? ":singular_table_id" : $with); + $joins[] = array('table'=>$table, 'on'=>":@table.{$single_table_id}=:table.id", 'columns'=>$columns); + } + + return $joins; + } + protected function has_one($association) { + if(!empty($this->rows[$this->current()->id]->$association)) return $this->rows[$this->current()->id]->$association; + if(!empty($this->has_one) && array_search($association, $this->has_one) !== false) { + $associate = new $association(); + $associating_column = "{$association}_id"; + $associate->find_by_id($this->$associating_column); + return $associate; + } else return false; + } + protected function has_many($association) { + if(!empty($this->rows[$this->current()->id]->$association)) return $this->rows[$this->current()->id]->$association; + if(!empty($this->has_many) && array_search($association, $this->has_many) !== false) { + $association_class = Inflector::singularize($association); + $associating_column = get_class($this); + $associate = new $association_class(); + $associate->find(array('where'=>array($associating_column . '_id=":id"', 'id'=>$this->id), 'order_by'=>$associate->default_order)); + return $associate; + } else return false; + } + + // events/callbacks + protected function before_find() {} + protected function after_find() {} + protected function before_save() {} + protected function after_save() {} + protected function before_delete() {} + protected function after_delete() {} + protected function before_create() {} + protected function after_create() {} + protected function before_update() {} + protected function after_update() {} + protected function before_next() {} + protected function after_next() {} + + // special events/callbacks + protected function after_find_for_each($row) { + return $row; + } + + // descructor + public function __destruct() { + // possibly put some serialization code in here? + // end; + } + } + + class ModelException extends StdException {} +?> \ No newline at end of file diff --git a/library/README b/library/README new file mode 100644 index 0000000..b5b3fcf --- /dev/null +++ b/library/README @@ -0,0 +1,7 @@ +== License + +Canvas Web Application Framework is released under the MIT license. + +== Support + +The Canvas homepage is http://c.anvas.es/. \ No newline at end of file diff --git a/library/Request.php b/library/Request.php new file mode 100755 index 0000000..7172a17 --- /dev/null +++ b/library/Request.php @@ -0,0 +1,163 @@ + + // @created 2005-10-14 + // @desc A class to handle Request variables + // @requires stdexception.php (StdException class) + // @refer_to http://www.php.net/manual/en/function.parse-str.php for parsing + // the URL parameters following ? before the # (e.g.: x?param=val#z) + + include_once('stdexception.php'); + + // classes + class Request { + // original request + public $url; + + // primary public variables + public $route; // the route requested (array of parameters passed in) + public $controller; // the Controller requested + public $action; // the Controller's requested Action + public $id; // the Id to be acted upon by the Controller's Action + + // location variables + public $protocol; // e.g.: 'http' or 'https' + public $location; // e.g.: 'http://anything.clayton.edu/omnia/files/index' // does not add params on to end + public $host; // e.g.: 'http://anything.clayton.edu/' + public $directory; // e.g.: 'omnia/' + public $request; // e.g.: 'files/list' + public $params_string; // e.g.: '?extra=foo' -> array of values + public $params; // array parsing of $_params + public $redirect_url; // e.g.: '/mvc/admin/' + public $referrer; // the 'sender' + + // continuation + public $continue_to; // e.g.: 'http://anything.clayton.edu/omnia/files/show/124' + + // request type + public $request_type; // e.g.: POST, GET, or XHR (XMLHTTPRequest) + public $xmlhttprequest; // a remote call + public $xhr; // alias of above + public $remote; // alias of above + + // protected variables + public $request_url; // the request (ie: the URL) + public $get; // PHP magic $_GET variable + public $post; // PHP magic $_POST variable + public $cookie; // PHP magic $_COOKIE variable + public $files; // PHP magic $_FILES variable + + // error variables + public $error; // error object + + // constructor + public function __construct($request_url) { + // set request string + $this->request_url = $request_url; + $this->process_request($this->request_url); + + // set default values + $this->post = new RequestIn($_POST); + $this->get = new RequestIn($_GET); + $this->cookie = new RequestIn($_COOKIE); + $this->files = new RequestIn($_FILES); + $this->server = new RequestIn($_SERVER); + $this->error = null; // why? + + // set location data + $this->protocol = (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') ? "http" : "https"; + $this->host = $_SERVER['HTTP_HOST']; + $this->directory = substr(str_replace("dispatcher.php", "", $_SERVER['SCRIPT_NAME']), 1); + $this->request = substr($_SERVER['PATH_INFO'], 1); + $this->params_string = $_SERVER['QUERY_STRING']; // http://www.php.net/manual/en/function.parse-str.php + parse_str($this->params_string, $params); // creates an associative array of the params + $this->params = $params; + $this->location = "{$this->protocol}://{$this->host}/{$this->directory}"; + $this->redirect_url = $_SERVER['REDIRECT_URL']; + $this->referrer = $_SERVER['HTTP_REFERER']; + + // if there was a continue_to parameter passed in after redirection, set the property + $session = Session::retreive(); + $this->continue_to = $session->continue_to; + $session->continue_to = null; + + // set properties of a remote call if applicable + if(!empty($_REQUEST['remote']) || !empty($_REQUEST['xhr']) || !empty($_REQUEST['xmlhttprequest'])) { + $this->remote = $this->xhr = $this->xmlhttprequest = true; + } + + // the original URL + $params = empty($this->params_string) ? "" : "?{$this->params_string}"; + $request_url = substr($request_url, 1); + $this->url = "{$this->protocol}://{$this->host}/{$this->directory}{$request_url}{$params}"; + + // possibly put some serialization code in here? + } + + // functions + private function process_request($request) { + // process routing + $route = Router2::route($request); + + // set routing variables + $this->route = $route; + foreach($route->mapping() as $routing_symbol) { + $this->$routing_symbol = $route->$routing_symbol; + } + } + + public function __set($name, $value) { + return false; // cannot set Request variables, it's just the nature of the Request object/variables + } + public function __get($name) { + if(isset($this->post->$name)) return $this->post->$name; + if(isset($this->get->$name)) return $this->get->$name; + if(isset($this->cookie->$name)) return $this->cookie->$name; + } + + // return all params (from the route and from the ?... params) + public function params() { + $params = array(); + + // get params from _GET + foreach($_GET as $get_param => $value) { + $params[$get_param] = $value; + } + + // getting routing values + foreach($this->route->mapping() as $routing_symbol) { + $params[$routing_symbol] = $this->route->$routing_symbol; + } + + return $params; + } + + // descructor + public function __destruct() { + // possibly put some serialization code in here? + // end; + } + } + + // specific request variables (POST, GET, etc) + class RequestIn { + private $values = array(); + + // constructor + public function __construct($values) { + foreach($values as $key=>$value) $this->values[$key] = $value; + } + + // accessors + public function __get($name) { + return $this->values[$name]; + } + public function __set($name, $value) { + return $this->values[$name] = $value; + } + } + + class RequestException extends StdException {} + +?> \ No newline at end of file diff --git a/library/Response.php b/library/Response.php new file mode 100755 index 0000000..d306837 --- /dev/null +++ b/library/Response.php @@ -0,0 +1,30 @@ + + // @created 2005-11-27 + // @desc Responds to the controller + // @requires stdexception.php (StdException class) + + include_once('stdexception.php'); + + class Response { + // public variables + public $response = array(); // container for the response data + public $error = null; // error object + + // functions + public function __set($name, $value) { + return $this->response[$name] = $value; + } + public function __get($name) { + return $this->response[$name]; + } + + public function respond() { + return $this->response; + } + } + + class ResponseException extends StdException { + } +?> \ No newline at end of file diff --git a/library/Router.php b/library/Router.php new file mode 100755 index 0000000..d157f82 --- /dev/null +++ b/library/Router.php @@ -0,0 +1,102 @@ + + // @created 2005-12-22 + // @desc A class to handle routing requests (breaking down request URLs to + // their property values, returned straight-up + // @requires stdexception.php (StdException class) + + include_once('library/stdexception.php'); + + // classes + class Router { + private $validators = array( + "name"=>'/[\w\d_\?!]+/', + "word"=>'/[\w\?!]+/', + "number"=>'/\d+/', + "date"=>'/(19|20)\d\d-[01]?\d-[0-3]?\d/', // e.g.: 2005-12-24 + "year"=>'/(19|20)\d\d/', // e.g.: 2005 (but not 2105, sorry) + "month"=>'/[01]?\d/', // e.g.: 12 (but not 22) + "day"=>'/[0-3]?\d/', // e.g.: 24 (but not 44) + "time"=>'/\d\d:\d\d(:\d\d)?/', + "filename"=>'/[\w\d_\.]+\.[\w\d]+/', + "anything"=>'/.*/', + + // format but optional + "optional name"=>'/[\w\d_\?!]*/', + "optional word"=>'/[\w\?!]*/', + "optional number"=>'/\d*/' + ); + + // functions + public static function route($request) { + // split $request into the components + $request = explode('/', ($request . ((substr($request, -1, 1) != '/') ? '/' : ''))); + array_shift($request); // get rid of the first, tempty element + + // get routes information from config file + $routes = Config::load('routes'); + $default_route = $routes['default_route']; + $routes['default_route'] = null; + + // loop through the $this->routes property, attempting to match a request + foreach($routes as $route) { + if($matched_route = self::match($request, $route)) break; + } + + if(empty($matched_route)) $matched_route = $default_route; + + // debugging + // crap // Debug::log(str_replace(' ', ' ', str_replace("\n", '', print_r($matched_route, true))), 'internal', 'info', 'Router'); + + return $matched_route; + } + + public static function map($route) { + // + } + + private static function match($request, $route) { + // create $router instance for properties + $router = new Router(); + + // split up route nodes (from '/controller/action' to an array of 'controller','action') + $route_nodes = explode('/', $route['route']); + + // loop through route and requested resource... return if it doesn't match/meet validation requirements, et al + while((list(, $request_node) = each($request)) && (list(, $route_node) = each($route_nodes))) { + // set the route name (if route just gives name and not explicit value, take out ':' in name and assign it to $route_name + if($route_node[0] == ':') $route_name = substr($route_node, 1); else $route_name = $route_node; + // if there's no specific validation spacified, set the validation pattern to a pre-defined constant for a valid string + if($route_node[0] == ':') $route_node = !empty($route['validate'][$route_name]) ? $router->validators[$route['validate'][$route_name]] : $router->validators['name']; + // handle missing/empty parameters (should be fixed to be more flexible!!!) + if(empty($request_node) && $route_name == "action") $request_node = "index"; + if(empty($request_node) && $route_name == "id") $request_node = "null"; + if(empty($request_node)) $request_node = !empty($route['default'][$route_name]) ? $route['default'][$route_name] : null; + // if its a plaintext entry in the route (for only working with certain requests), add /s around the word to make it a valid PCRE regex pattern + if($route_node[0] != '/') $route_node = "/{$route_node}/"; + + // debugging + // this is really crappy // Debug::log("route name: '{$route_name}', route node: '{$route_node}', request node: '{$request_node}';", 'internal', 'low', 'Router'); + + // test to see if it matches; if not, return null to signify that the request did not match the route, or return the actual route + if($request_node == null || preg_match($route_node, $request_node) != 1) { + return null; // no match + } else { + // if it's an ID with the value of "null", convert it to the actual null value + if($route_name == "id" && $request_node == "null") $request_node = null; + // add data to route array if it matched alright + $matched_route[$route_name] = $request_node; + } + } + + // add the routing map to the matched route + $matched_route['_map'] = $route; + + // return matched route + return $matched_route; + } + } + + class RoutesException extends StdException {} +?> \ No newline at end of file diff --git a/library/Router2.php b/library/Router2.php new file mode 100644 index 0000000..f52891e --- /dev/null +++ b/library/Router2.php @@ -0,0 +1,173 @@ + + // @created_on 23 Apr 2006 + + class Router2 { + // internal static variables + private static $routes = array(); // the routes provided to route against + private static $route = null; // the route for the request + + // regular expressions for validations + private static $validations = array( + // primary + 'controller'=>'([\w_]+)', + 'action'=>'([\w_]+)', + 'id'=>'([\d]*)?', // makes the :id optional + + // character/text + 'word'=>'([\w\d_+-\.\?]+)', + + // numeric + 'numeric'=>'([\d]+)', + + // specialty + 'multiple'=>'((%s\/?)+)', + + // dates + 'date'=>'(\d\d\d\d-(0[1-9]|1[1-2])-([0-2][0-9]|3[0-2]))', + 'year'=>'(\d\d\d\d)', + 'month'=>'(0?[1-9]|1[1-2])', + 'day'=>'(0?[1-9]|[1-2][0-9]|3[0-2])' + ); + + // route request + public static function route($request) { + // compare the request against the available routes + foreach(self::$routes as $route=>$options) { + if(preg_match("/^{$route}$/", $request, $matches) == 1) break; + } + + // map values into $map + foreach($options['values'] as $name=>$value) { + $map[$name] = $value; + } + array_shift($matches); + foreach($matches as $key=>$match) { + if($options['names'][$key][strlen($options['names'][$key]) - 1] != '*') { + // not a multiple name... (it's not sucking up the rest of the values) + $map[$options['names'][$key]] = $match; + } else { + $map[substr($options['names'][$key], 0, -1)] = explode('/', $match); + break; + } + } + + // create new Route object and give it to self::$route, then return it + self::$route = new Route($route, $map); + return self::$route; + } + + // add route + public static function map($route, $values = array(), $validates = array()) { + // @description Takes a route, like ':controller/:action/:id', and values to include in the route when not specified + // bythe actual route itself. This is handy for complex requests with simplified routes. + + $request = $route; + + // parse $route into regex + if($route[0] == '/') $route = substr($route, 1); // strip out initial slash + $route = explode('/', $route); // pull route into its parts + foreach($route as $key=>$part) { + // pull out names (start with a ':') + if($part[0] == ':') { + $name = substr($part, 1); + $names[] = $name; + } else { + // literal value + $name = $part; + } + + // put in regular expression pieces (opting for whatever is in $validates, using 'word' as default unless it's a literal) + if(self::$validations[$name]) $test = self::$validations[$name]; // default values + if($validates[$name]) $test = self::$validations[$validates[$name]]; // use specific validations as often as possible + if($part[0] != ':') $test = $part; // just keep literals + if(empty($test)) $test = self::$validations['word']; // last resort + if($name[strlen($name) - 1] == '*') $test = sprintf(self::$validations['multiple'], $test); + $route[$key] = $test; + $test = ''; // reset $test; + } + + // assemble new regular expression route for testing + $route = implode('\/', $route); + + // assign $routes with appropriate data + self::$routes[$route] = array('route'=>$request, 'names'=>$names, 'values'=>$values, 'validates'=>$validates); + + return $route; + } + + // generate a route, given a Route object + public static function url_for($request = array()) { + // loop through routes, matching them + foreach(self::$routes as $route=>$options) { + // default to none found + $found = false; + + // find an appropriate route + if(is_array($request)) { + // if it's an array + foreach(array_keys($request) as $key) { + if((is_array($options['names']) && array_search($key, $options['names']) !== false) || ($options['values'][$key] == $request[$key])) { + $found = true; + } else { + $found = false; + break; + } + } + if($found == true) break; + } else { + // if it's a string, return the routing data + if(preg_match("/^{$route}$/", $request) > 0) { + return array($route=>$options); + } + } + } + + if($found) { + // create new URL + + // get the route (to form the URL) + $url = $options['route']; // the route + + // loop through the names, inserting the values into the route/URL + foreach($options['names'] as $name) { + $url = str_replace(":{$name}", $request[$name], $url); + } + + // that's it! the Request or Response objects will handle the domain data (because that's their domain!) + + return $url; + } else { + // return failure + } + } + } + + // the tangible, active part of the routing system, representing an actual route + class Route { + // variables + private $map = array(); // the route mapping + private $route = ''; // the route requested + + // magic functions + public function __get($name) { + return $this->map[$name]; + } + public function __set($name, $value) { + return $this->map[$name] = $value; + } + + // give the route's mapping + public function mapping() { + return array_keys($this->map); + } + + // constructor + public function __construct($route, $map) { + $this->route = $route; + $this->map = $map; + } + } +?> \ No newline at end of file diff --git a/library/Session.php b/library/Session.php new file mode 100755 index 0000000..f0f6372 --- /dev/null +++ b/library/Session.php @@ -0,0 +1,76 @@ + + // @created 2005-09-26 + // @desc A session class for easily accessing and administering sessions (can + // transparently keep all session data in the database) + // @requires stdexception.php (StdException class) + + include_once('stdexception.php'); + + // constants + // hash type constants +// define("AUTH_HASHTYPE_SHA", 2); +// define("AUTH_HASHTYPE_MD5", 5); + + // classes + class Session { + // protected variables + private $strSID; // the session ID + + // retreive an instance of the Session object (not a singleton, more of a factory of sorts) + public static function retreive() { + return new Session(); + } + + // initialize + public static function initialize() { + // session_id("thehub"); // must be unique + session_name("thehub"); + session_start(); + } + public static function flush() { + session_write_close(); + } + public static function destroy() { + $_SESSION = array(); // empty session + setcookie(session_name(), '', time()-42000, '/'); // destroys associated session cookie + unset($_COOKIE[session_name()]); // just in case + session_destroy(); // destroys the session + session_start(); // overwrite it, yeah! + + return true; + } + + public static function flash() { + if(empty($_SESSION['flash'])) { + return null; + } else { + $flash = $_SESSION['flash']; + $_SESSION['flash'] = null; + return $flash; + } + } + public static function continue_to($url) { + $_SESSION['continue_to'] = $url; + } + + // functions + public function __set($name, $value) { + // setcookie($name, $value->username, (15 * 60), ".clayton.edu", "/"); + $_SESSION[$name] = $value; + } + public function __get($name) { + return $_SESSION[$name]; + } + + // descructor + public function __destruct() { + // possibly put some serialization code in here? + // end; + } + } + + class SessionException extends StdException { + } +?> \ No newline at end of file diff --git a/library/View.php b/library/View.php new file mode 100755 index 0000000..26a4b95 --- /dev/null +++ b/library/View.php @@ -0,0 +1,78 @@ +controller; + $helper_name = file_exists("{$response->controller}_helper") ? "{$response->controller}_helper" : "application_helper"; + + $smarty = new Smarty(); + + $smarty->left_delimiter = "<" . "%"; // split them up as to keep them from parsing accidentally + $smarty->right_delimiter = "%". ">"; // ditto + $smarty->template_dir = "./views/{$view_name}/"; + $smarty->compile_dir = "./views/{$view_name}/compile/"; + $smarty->cache_dir = "./views/{$view_name}/cache/"; + $smarty->config_dir = "./views/{$view_name}/config/"; + + // register helper functions + self::register_helper_functions($helper_name, $smarty); + + // register while block + $smarty->register_block('while', array(get_class(), 'while_block')); + + $smarty->assign('response', $response); + foreach($response->respond() as $key=>$value) { + // if($key == "response") continue; + $smarty->assign($key, $value); + } + + // render layout if set, action template if not... + if($response->layout == null) { + $smarty->display($response->template); + } else { + $smarty->display($response->layout); + } + } + + // this static method will analyze the methods of the helper class that is to be associated + // with the views and will register these functions appropriately (automating the registration + // process) + private static function register_helper_functions($helper_name, &$smarty) { + $helper_class = new ReflectionClass($helper_name); + $filters = self::find_helper_filters($helper_class); + foreach($helper_class->getMethods() as $method) { + if(in_array($method->name, $filters)) { + $smarty->register_modifier($method->name, array($helper_name, $method->name)); continue; // register filters as such + } + $smarty->register_function($method->name, array($helper_name, $method->name)); + } + } + private static function find_helper_filters($helper_class) { + $properties = $helper_class->getProperties(); + $helper_class_name = $helper_class->getName(); + $helper_instance = new $helper_class_name(); + foreach($properties as $filter) { + if($filter->getName() == "treat_as_filter") return $filter->getValue($helper_instance); + } + } + + // custom tags/functions for Smarty + public static function while_block($params, $content, &$smarty, &$repeat) { + $name = $params['name']; + $value = $params['value']; + $$name = $value; + return $content; + } + } + + class ViewException extends StdException {} +?> \ No newline at end of file diff --git a/library/YAML.php b/library/YAML.php new file mode 100755 index 0000000..39fde97 --- /dev/null +++ b/library/YAML.php @@ -0,0 +1,25 @@ + + // @created 2006-02-08 + // @desc Handles parsing YAML formatted documents + // @refer_to "SPYC":http://spyc.sourceforge.net/ + // @requires YAML/spyc.php5 (YAML PHP5 parser class) + // @requires extexception.php (StdException class) + + include_once('YAML/spyc.php'); + include_once('stdexception.php'); + + // classes + class YAML { + // loads and parses a YAML document + public static function load($yaml) { + $output = Spyc::YAMLLoad($yaml); + return $output; + } + } + + class YAMLFileNotFound extends StdException {} + class YAMLException extends StdException {} +?> \ No newline at end of file diff --git a/library/YAML/CHANGES b/library/YAML/CHANGES new file mode 100755 index 0000000..4f6ea19 --- /dev/null +++ b/library/YAML/CHANGES @@ -0,0 +1,45 @@ +# +# S P Y C +# a simple php yaml class +# v0.2(.2) +# +# Load this file! +# >> $changes = Spyc::YAMLLoad('CHANGES'); +# +--- %YAML:1.1 +title: Spyc -- a Simple PHP YAML Class +version: 0.2.2 +author: [chris wanstrath, chris@ozmm.org] +websites: [http://www.yaml.org, http://spyc.sourceforge.net/] +license: [MIT License, http://www.opensource.org/licenses/mit-license.php] +copyright: (c) 2005-2006 Chris Wanstrath + +changes: + 0.2.2: + - Implemented Mustafa Kumas' changes: + - Improved regex in _inlineEscape() method. + - Improved handling of escaped single and double quotes within strings. + + 0.2.1: + - Implemented Fabien Potencier's changes: + - Fixed warnings reported by E_STRICT + - More complete YAML boolean support (on/off yes/no y/n) + - Produce error when someone tries to use tabs instead of spaces. + - Moved array_kmerge function into a private method. + - Added PHP5 strict version of class (spyc.php5). + - Added line break preservation in dump() + - Added some string checking sanity before calling file_exists() + + 0.2: + - Hashes (#) can now be used in strings, and comments work better. + - Fixed warning about non-object. + - Numeric keys are now allowed. + - '"key" : value' is now allowed (space after quoted key name) + - Underscore (_) and other crazy characters now allowed in key names of + dumped arrays. + - Added options/parameters to change indent and wordwrap length in dump() + - Added a really primitive, lo-fi output test script. + + 0.1.1: + - Fixed notices that were being generated when set to E_ALL. + - Defined some private properties like a good OOPer. diff --git a/library/YAML/README b/library/YAML/README new file mode 100755 index 0000000..78fb329 --- /dev/null +++ b/library/YAML/README @@ -0,0 +1,162 @@ +# +# S P Y C +# a simple php yaml class +# v0.2(.2) +# +# Load this README! +# >> $readme = Spyc::YAMLLoad('README'); +# +--- %YAML:1.1 +title: Spyc -- a Simple PHP YAML Class +version: 0.2.2 +author: [chris wanstrath, chris@ozmm.org] +websites: [http://www.yaml.org, http://spyc.sourceforge.net] +license: [MIT License, http://www.opensource.org/licenses/mit-license.php] +copyright: (c) 2005-2006 Chris Wanstrath +tested on: [php 4.3.11, php 5.0.4] + +installation: > + Copy spyc.php to a directory you can + access with your YAML-ready PHP script. + + That's it! + +about: > + From www.yaml.org: + + "YAML(tm) (rhymes with 'camel') is a human-friendly, cross language, + Unicode based data serialization language designed around the common + native data structures of agile programming languages. It is broadly + useful for programming needs ranging from configuration files to + Internet messaging to object persistence to data auditing. Together + with the Unicode standard for characters, the YAML specification provides + all the information necessary to understand YAML Version 1.1 and to + creating programs that process YAML information. + + YAML(tm) is a balance of the following design goals: + - YAML documents are very readable by humans. + - YAML interacts well with scripting languages. + - YAML uses host languages' native data structures. + - YAML has a consistent information model. + - YAML enables stream-based processing. + - YAML is expressive and extensible. + - YAML is easy to implement." + + YAML makes a lot of sense. It's easy to use, easy to learn, and cool. + As the lucky stiff named why once said, "YAML is a beacon of light." + + If you're new to YAML, may we suggest YAML In Five Minutes: + - http://yaml.kwiki.org/?YamlInFiveMinutes + + If you don't have five minutes, realize that this README is a completely + valid YAML document. Dig in, load this or any YAML file into an array + with Spyc and see how easy it is to translate friendly text into usable + data. + + The purpose of Spyc is to provide a pure PHP alternative to Syck, a + simple API for loading and dumping YAML documents, a YAML loader which + understands a usable subset of the YAML spec, and to further spread + the glory of YAML to the PHP masses. + + If you're at all hesitant ("usable subset of YAML?!"), navigate + http://yaml.org/start.html. Spyc completely understands the YAML + document shown there, a document which has features way beyond the + scope of what normal config files might require. Try it for yourself, + and then start enjoying the peace of mind YAML brings to your life. + +meat and a few potatoes: + - concept: Loading a YAML document into PHP + brief: > + $yaml will become an array of all the data in wicked.yml + code: | + + include('spyc.php'); + + $yaml = Spyc::YAMLLoad('wicked.yml'); + + - concept: Loading a YAML string into PHP + brief: > + $array will look like this: + array('A YAML','document in a','string') + code: | + + include('spyc.php'); + + $yaml = '- A YAML\n- document in a\n- string.'; + $array = Spyc::YAMLLoad($yaml); + + - concept: Dumping a PHP array to YAML + brief: > + $yaml will become a string of a YAML document created from + $array. + code: | + + include('spyc.php'); + + $array['name'] = 'chris'; + $array['sport'] = 'curbing'; + + $yaml = Spyc::YAMLDump($array); + +prior art: + - who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki] + why?: > + The YAML spec is really a piece of work, and these guys + did a great job on it. A simple and elegant language like + YAML was a long time coming and it's refreshing to know + such able minded individuals took the task to heart and + executed it with cunning and strength. In addition to + their various noteworthy contributions to YAML parsers + and related projects, YAML.pm's README is a treasure trove + of information for knowledge seekers. Thanks, guys. + + - who: why the lucky stiff + why?: > + As the author of Syck, the code used in Ruby for the language's + YAML class and methods, why is indirectly (directly?) responsible + for my first exposure to YAML (as a config file in a Ruby web-app) + and the countless hours I spent playing with this sheik new data + format afterwards. Syck's README is a YAML file and thus the + inspiration for this file and, even, this very piece of software. + + - who: Steve Howell + why?: > + Python's YAML implementation. PyYAML's README file is also YAML, + so it too inspired the YAML format of this README file. + + - who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al] + why?: > + PHP is great at what it does best. It's also paid a lot of my bills. + Thanks. + +bugs: + report: > + Please see Spyc's Sourceforge project page for information on reporting bugs. + speed: > + This implementation was not designed for speed. Rather, it + was designed for those who need a pure PHP implementation of + a YAML parser and who are not overly concerned with performance. + If you want speed, check out Syck. + depth: > + This parser is by no means a comprehensive YAML parser. For supported + features and future plans, check the website. + unicode: > + YAML is supposed to be unicode, but for now we're just using ASCII. + PHP has crappy unicode support but who knows what the future holds. + +resources: + - http://www.yaml.org + - http://www.yaml.org/spec/ + - http://yaml.kwiki.org/?YamlInFiveMinutes + - http://www.whytheluckystiff.net/syck/ + - http://yaml4r.sourceforge.net/cookbook/ + - http://www.sourceforge.net/projects/spyc/ + - http://spyc.sourceforge.net/ + +thanks: + - Adam Wood + - Daniel Ferreira + - Aaron Jensen + - Mike Thornton + - Fabien Potencier + - Mustafa Kumas \ No newline at end of file diff --git a/library/YAML/spyc.php b/library/YAML/spyc.php new file mode 100755 index 0000000..328b7c4 --- /dev/null +++ b/library/YAML/spyc.php @@ -0,0 +1,853 @@ + + * @link http://spyc.sourceforge.net/ + * @copyright Copyright 2005-2006 Chris Wanstrath + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + + /** + * A node, used by Spyc for parsing YAML. + * @package Spyc + */ + class YAMLNode { + /**#@+ + * @access public + * @var string + */ + public $parent; + public $id; + /**#@+*/ + /** + * @access public + * @var mixed + */ + public $data; + /** + * @access public + * @var int + */ + public $indent; + /** + * @access public + * @var bool + */ + public $children = false; + + /** + * The constructor assigns the node a unique ID. + * @access public + * @return void + */ + public function YAMLNode() { + $this->id = uniqid(''); + } + } + + /** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load($file); + * + * @package Spyc + */ + class Spyc { + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * + * $array = Spyc::YAMLLoad('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + public function YAMLLoad($input) { + $spyc = new Spyc; + return $spyc->load($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public function YAMLDump($array,$indent = false,$wordwrap = false) { + $spyc = new Spyc; + return $spyc->dump($array,$indent,$wordwrap); + } + + /** + * Load YAML into a PHP array from an instantiated object + * + * The load method, when supplied with a YAML stream (string or file path), + * will do its best to convert the YAML into a PHP array. Pretty simple. + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + public function load($input) { + // See what type of input we're talking about + // If it's not a file, assume it's a string + if (!empty($input) && (strpos($input, "\n") === false) + && file_exists($input)) { + $yaml = file($input); + } else { + $yaml = explode("\n",$input); + } + // Initiate some objects and values + $base = new YAMLNode; + $base->indent = 0; + $this->_lastIndent = 0; + $this->_lastNode = $base->id; + $this->_inBlock = false; + $this->_isInline = false; + + foreach ($yaml as $linenum => $line) { + $ifchk = trim($line); + + // If the line starts with a tab (instead of a space), throw a fit. + if (preg_match('/^(\t)+(\w+)/', $line)) { + $err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'. + ' with a tab. YAML only recognizes spaces. Please reformat.'; + die($err); + } + + if ($this->_inBlock === false && empty($ifchk)) { + continue; + } elseif ($this->_inBlock == true && empty($ifchk)) { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] .= "\n"; + } elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') { + // Create a new node and get its indent + $node = new YAMLNode; + $node->indent = $this->_getIndent($line); + + // Check where the node lies in the hierarchy + if ($this->_lastIndent == $node->indent) { + // If we're in a block, add the text to the parent's data + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } else { + // The current node's parent is the same as the previous node's + if (isset($this->_allNodes[$this->_lastNode])) { + $node->parent = $this->_allNodes[$this->_lastNode]->parent; + } + } + } elseif ($this->_lastIndent < $node->indent) { + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } elseif ($this->_inBlock === false) { + // The current node's parent is the previous node + $node->parent = $this->_lastNode; + + // If the value of the last node's data was > or | we need to + // start blocking i.e. taking in all lines as a text value until + // we drop our indent. + $parent =& $this->_allNodes[$node->parent]; + $this->_allNodes[$node->parent]->children = true; + if (is_array($parent->data)) { + $chk = $parent->data[key($parent->data)]; + if ($chk === '>') { + $this->_inBlock = true; + $this->_blockEnd = ' '; + $parent->data[key($parent->data)] = + str_replace('>','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line).' '; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } elseif ($chk === '|') { + $this->_inBlock = true; + $this->_blockEnd = "\n"; + $parent->data[key($parent->data)] = + str_replace('|','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line)."\n"; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } + } + } + } elseif ($this->_lastIndent > $node->indent) { + // Any block we had going is dead now + if ($this->_inBlock === true) { + $this->_inBlock = false; + if ($this->_blockEnd = "\n") { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] = + trim($last->data[key($last->data)]); + } + } + + // We don't know the parent of the node so we have to find it + // foreach ($this->_allNodes as $n) { + foreach ($this->_indentSort[$node->indent] as $n) { + if ($n->indent == $node->indent) { + $node->parent = $n->parent; + } + } + } + + if ($this->_inBlock === false) { + // Set these properties with information from our current node + $this->_lastIndent = $node->indent; + // Set the last node + $this->_lastNode = $node->id; + // Parse the YAML line and return its data + $node->data = $this->_parseLine($line); + // Add the node to the master list + $this->_allNodes[$node->id] = $node; + // Add a reference to the node in an indent array + $this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id]; + // Add a reference to the node in a References array if this node + // has a YAML reference in it. + if ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (!is_array($node->data[key($node->data)])) ) + && + ( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)])) + || + (preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } elseif ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (is_array($node->data[key($node->data)])) ) + ) { + // Incomplete reference making code. Ugly, needs cleaned up. + foreach ($node->data[key($node->data)] as $d) { + if ( !is_array($d) && + ( (preg_match('/^&([^ ]+)/',$d)) + || + (preg_match('/^\*([^ ]+)/',$d)) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } + } + } + } + } + } + unset($node); + + // Here we travel through node-space and pick out references (& and *) + $this->_linkReferences(); + + // Build the PHP array out of node-space + $trunk = $this->_buildArray(); + return $trunk; + } + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public function dump($array,$indent = false,$wordwrap = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = "---\n"; + + // Start at the base of the array and move through it. + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,0); + } + return $string; + } + + /**** Private Properties ****/ + + /**#@+ + * @access private + * @var mixed + */ + private $_haveRefs; + private $_allNodes; + private $_lastIndent; + private $_lastNode; + private $_inBlock; + private $_isInline; + private $_dumpIndent; + private $_dumpWordWrap; + /**#@+*/ + + /**** Private Methods ****/ + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _yamlize($key,$value,$indent) { + if (is_array($value)) { + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key,NULL,$indent); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key,$value,$indent); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + private function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,$indent); + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _dumpNode($key,$value,$indent) { + // do some folding here, for blocks + if (strpos($value,"\n")) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + } + + $spaces = str_repeat(' ',$indent); + + if (is_int($key)) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + // It's mapped + $string = $spaces.$key.': '.$value."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + private function _doLiteralBlock($value,$indent) { + $exploded = explode("\n",$value); + $newValue = '|'; + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $newValue .= "\n" . $spaces . trim($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + private function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + if ($this->_dumpWordWrap === 0) { + return $value; + } + + if (strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } + return $value; + } + + /* Methods used in loading */ + + /** + * Finds and returns the indentation of a YAML line + * @access private + * @return int + * @param string $line A line from the YAML file + */ + private function _getIndent($line) { + preg_match('/^\s{1,}/',$line,$match); + if (!empty($match[0])) { + $indent = substr_count($match[0],' '); + } else { + $indent = 0; + } + return $indent; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + private function _parseLine($line) { + $line = trim($line); + + $array = array(); + + if (preg_match('/^-(.*):$/',$line)) { + // It's a mapped sequence + $key = trim(substr(substr($line,1),0,-1)); + $array[$key] = ''; + } elseif ($line[0] == '-' && substr($line,0,3) != '---') { + // It's a list item but not a new stream + if (strlen($line) > 1) { + $value = trim(substr($line,1)); + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + $array[] = $value; + } else { + $array[] = array(); + } + } elseif (preg_match('/^(.+):/',$line,$key)) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(':',$line); + $key = trim($explode[0]); + array_shift($explode); + $value = trim(implode(':',$explode)); + } + + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if (empty($key)) { + $array[] = $value; + } else { + $array[$key] = $value; + } + } + return $array; + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + private function _toType($value) { + if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) { + $value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches)); + $value = preg_replace('/\\\\"/','"',$value); + } elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) { + // Inline Sequence + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + } elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) { + // It's a map + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + $value = array($key => $value); + } elseif (preg_match("/{(.+)}$/",$value,$matches)) { + // Inline Mapping + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $array = array(); + foreach ($explode as $v) { + $array = $array + $this->_toType($v); + } + $value = $array; + } elseif (strtolower($value) == 'null' or $value == '' or $value == '~') { + $value = NULL; + } elseif (ctype_digit($value)) { + $value = (int)$value; + } elseif (in_array(strtolower($value), + array('true', 'on', '+', 'yes', 'y'))) { + $value = TRUE; + } elseif (in_array(strtolower($value), + array('false', 'off', '-', 'no', 'n'))) { + $value = FALSE; + } elseif (is_numeric($value)) { + $value = (float)$value; + } else { + // Just a normal string, right? + $value = trim(preg_replace('/#(.+)$/','',$value)); + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + private function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $strings = $strings[2]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + // Check for sequences + if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) { + $inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline); + $seqs = $seqs[0]; + } + + // Check for mappings + if (preg_match_all('/{(.+)}/U',$inline,$maps)) { + $inline = preg_replace('/{(.+)}/U','YAMLMap',$inline); + $maps = $maps[0]; + } + + $explode = explode(', ',$inline); + + // Re-add the strings + if (!empty($strings)) { + $i = 0; + foreach ($explode as $key => $value) { + if ($value == 'YAMLString') { + $explode[$key] = $strings[$i]; + ++$i; + } + } + } + + // Re-add the sequences + if (!empty($seqs)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value); + ++$i; + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + $explode[$key] = str_replace('YAMLMap',$maps[$i],$value); + ++$i; + } + } + } + + return $explode; + } + + /** + * Builds the PHP array from all the YAML nodes we've gathered + * @access private + * @return array + */ + private function _buildArray() { + $trunk = array(); + + if (!isset($this->_indentSort[0])) { + return $trunk; + } + + foreach ($this->_indentSort[0] as $n) { + if (empty($n->parent)) { + $this->_nodeArrayizeData($n); + // Check for references and copy the needed data to complete them. + $this->_makeReferences($n); + // Merge our data with the big array we're building + $trunk = $this->_array_kmerge($trunk,$n->data); + } + } + + return $trunk; + } + + /** + * Traverses node-space and sets references (& and *) accordingly + * @access private + * @return bool + */ + private function _linkReferences() { + if (is_array($this->_haveRefs)) { + foreach ($this->_haveRefs as $node) { + if (!empty($node->data)) { + $key = key($node->data); + // If it's an array, don't check. + if (is_array($node->data[$key])) { + foreach ($node->data[$key] as $k => $v) { + $this->_linkRef($node,$key,$k,$v); + } + } else { + $this->_linkRef($node,$key); + } + } + } + } + return true; + } + + function _linkRef(&$n,$key,$k = NULL,$v = NULL) { + if (empty($k) && empty($v)) { + // Look for &refs + if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key] = + substr($n->data[$key],strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } elseif (!empty($k) && !empty($v)) { + if (preg_match('/^&([^ ]+)/',$v,$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key][$k] = + substr($v,strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } + } + + /** + * Finds the children of a node and aids in the building of the PHP array + * @access private + * @param int $nid The id of the node whose children we're gathering + * @return array + */ + private function _gatherChildren($nid) { + $return = array(); + $node =& $this->_allNodes[$nid]; + foreach ($this->_allNodes as $z) { + if ($z->parent == $node->id) { + // We found a child + $this->_nodeArrayizeData($z); + // Check for references + $this->_makeReferences($z); + // Merge with the big array we're returning + // The big array being all the data of the children of our parent node + $return = $this->_array_kmerge($return,$z->data); + } + } + return $return; + } + + /** + * Turns a node's data and its children's data into a PHP array + * + * @access private + * @param array $node The node which you want to arrayize + * @return boolean + */ + private function _nodeArrayizeData(&$node) { + if (is_array($node->data) && $node->children == true) { + // This node has children, so we need to find them + $childs = $this->_gatherChildren($node->id); + // We've gathered all our children's data and are ready to use it + $key = key($node->data); + $key = empty($key) ? 0 : $key; + // If it's an array, add to it of course + if (is_array($node->data[$key])) { + $node->data[$key] = $this->_array_kmerge($node->data[$key],$childs); + } else { + $node->data[$key] = $childs; + } + } elseif (!is_array($node->data) && $node->children == true) { + // Same as above, find the children of this node + $childs = $this->_gatherChildren($node->id); + $node->data = array(); + $node->data[] = $childs; + } + + // We edited $node by reference, so just return true + return true; + } + + /** + * Traverses node-space and copies references to / from this object. + * @access private + * @param object $z A node whose references we wish to make real + * @return bool + */ + private function _makeReferences(&$z) { + // It is a reference + if (isset($z->ref)) { + $key = key($z->data); + // Copy the data to this object for easy retrieval later + $this->ref[$z->ref] =& $z->data[$key]; + // It has a reference + } elseif (isset($z->refKey)) { + if (isset($this->ref[$z->refKey])) { + $key = key($z->data); + // Copy the data from this object to make the node a real reference + $z->data[$key] =& $this->ref[$z->refKey]; + } + } + return true; + } + + + /** + * Merges arrays and maintains numeric keys. + * + * An ever-so-slightly modified version of the array_kmerge() function posted + * to php.net by mail at nospam dot iaindooley dot com on 2004-04-08. + * + * http://us3.php.net/manual/en/function.array-merge.php#41394 + * + * @access private + * @param array $arr1 + * @param array $arr2 + * @return array + */ + private function _array_kmerge($arr1,$arr2) { + if(!is_array($arr1)) + $arr1 = array(); + + if(!is_array($arr2)) + $arr2 = array(); + + $keys1 = array_keys($arr1); + $keys2 = array_keys($arr2); + $keys = array_merge($keys1,$keys2); + $vals1 = array_values($arr1); + $vals2 = array_values($arr2); + $vals = array_merge($vals1,$vals2); + $ret = array(); + + foreach($keys as $key) { + list($unused,$val) = each($vals); + // This is the good part! If a key already exists, but it's part of a + // sequence (an int), just keep addin numbers until we find a fresh one. + if (isset($ret[$key]) and is_int($key)) { + while (array_key_exists($key, $ret)) { + $key++; + } + } + $ret[$key] = $val; + } + + return $ret; + } + } +?> \ No newline at end of file diff --git a/library/YAML/spyc.php4 b/library/YAML/spyc.php4 new file mode 100755 index 0000000..6c06465 --- /dev/null +++ b/library/YAML/spyc.php4 @@ -0,0 +1,853 @@ + + * @link http://spyc.sourceforge.net/ + * @copyright Copyright 2005-2006 Chris Wanstrath + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + + /** + * A node, used by Spyc for parsing YAML. + * @package Spyc + */ + class YAMLNode { + /**#@+ + * @access public + * @var string + */ + var $parent; + var $id; + /**#@+*/ + /** + * @access public + * @var mixed + */ + var $data; + /** + * @access public + * @var int + */ + var $indent; + /** + * @access public + * @var bool + */ + var $children = false; + + /** + * The constructor assigns the node a unique ID. + * @access public + * @return void + */ + function YAMLNode() { + $this->id = uniqid(''); + } + } + + /** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load($file); + * + * @package Spyc + */ + class Spyc { + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * + * $array = Spyc::YAMLLoad('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + function YAMLLoad($input) { + $spyc = new Spyc; + return $spyc->load($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + function YAMLDump($array,$indent = false,$wordwrap = false) { + $spyc = new Spyc; + return $spyc->dump($array,$indent,$wordwrap); + } + + /** + * Load YAML into a PHP array from an instantiated object + * + * The load method, when supplied with a YAML stream (string or file path), + * will do its best to convert the YAML into a PHP array. Pretty simple. + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + function load($input) { + // See what type of input we're talking about + // If it's not a file, assume it's a string + if (!empty($input) && (strpos($input, "\n") === false) + && file_exists($input)) { + $yaml = file($input); + } else { + $yaml = explode("\n",$input); + } + // Initiate some objects and values + $base = new YAMLNode; + $base->indent = 0; + $this->_lastIndent = 0; + $this->_lastNode = $base->id; + $this->_inBlock = false; + $this->_isInline = false; + + foreach ($yaml as $linenum => $line) { + $ifchk = trim($line); + + // If the line starts with a tab (instead of a space), throw a fit. + if (preg_match('/^(\t)+(\w+)/', $line)) { + $err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'. + ' with a tab. YAML only recognizes spaces. Please reformat.'; + die($err); + } + + if ($this->_inBlock === false && empty($ifchk)) { + continue; + } elseif ($this->_inBlock == true && empty($ifchk)) { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] .= "\n"; + } elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') { + // Create a new node and get its indent + $node = new YAMLNode; + $node->indent = $this->_getIndent($line); + + // Check where the node lies in the hierarchy + if ($this->_lastIndent == $node->indent) { + // If we're in a block, add the text to the parent's data + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } else { + // The current node's parent is the same as the previous node's + if (isset($this->_allNodes[$this->_lastNode])) { + $node->parent = $this->_allNodes[$this->_lastNode]->parent; + } + } + } elseif ($this->_lastIndent < $node->indent) { + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } elseif ($this->_inBlock === false) { + // The current node's parent is the previous node + $node->parent = $this->_lastNode; + + // If the value of the last node's data was > or | we need to + // start blocking i.e. taking in all lines as a text value until + // we drop our indent. + $parent =& $this->_allNodes[$node->parent]; + $this->_allNodes[$node->parent]->children = true; + if (is_array($parent->data)) { + $chk = $parent->data[key($parent->data)]; + if ($chk === '>') { + $this->_inBlock = true; + $this->_blockEnd = ' '; + $parent->data[key($parent->data)] = + str_replace('>','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line).' '; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } elseif ($chk === '|') { + $this->_inBlock = true; + $this->_blockEnd = "\n"; + $parent->data[key($parent->data)] = + str_replace('|','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line)."\n"; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } + } + } + } elseif ($this->_lastIndent > $node->indent) { + // Any block we had going is dead now + if ($this->_inBlock === true) { + $this->_inBlock = false; + if ($this->_blockEnd = "\n") { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] = + trim($last->data[key($last->data)]); + } + } + + // We don't know the parent of the node so we have to find it + // foreach ($this->_allNodes as $n) { + foreach ($this->_indentSort[$node->indent] as $n) { + if ($n->indent == $node->indent) { + $node->parent = $n->parent; + } + } + } + + if ($this->_inBlock === false) { + // Set these properties with information from our current node + $this->_lastIndent = $node->indent; + // Set the last node + $this->_lastNode = $node->id; + // Parse the YAML line and return its data + $node->data = $this->_parseLine($line); + // Add the node to the master list + $this->_allNodes[$node->id] = $node; + // Add a reference to the node in an indent array + $this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id]; + // Add a reference to the node in a References array if this node + // has a YAML reference in it. + if ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (!is_array($node->data[key($node->data)])) ) + && + ( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)])) + || + (preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } elseif ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (is_array($node->data[key($node->data)])) ) + ) { + // Incomplete reference making code. Ugly, needs cleaned up. + foreach ($node->data[key($node->data)] as $d) { + if ( !is_array($d) && + ( (preg_match('/^&([^ ]+)/',$d)) + || + (preg_match('/^\*([^ ]+)/',$d)) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } + } + } + } + } + } + unset($node); + + // Here we travel through node-space and pick out references (& and *) + $this->_linkReferences(); + + // Build the PHP array out of node-space + $trunk = $this->_buildArray(); + return $trunk; + } + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + function dump($array,$indent = false,$wordwrap = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = "---\n"; + + // Start at the base of the array and move through it. + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,0); + } + return $string; + } + + /**** Private Properties ****/ + + /**#@+ + * @access private + * @var mixed + */ + var $_haveRefs; + var $_allNodes; + var $_lastIndent; + var $_lastNode; + var $_inBlock; + var $_isInline; + var $_dumpIndent; + var $_dumpWordWrap; + /**#@+*/ + + /**** Private Methods ****/ + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _yamlize($key,$value,$indent) { + if (is_array($value)) { + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key,NULL,$indent); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key,$value,$indent); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,$indent); + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _dumpNode($key,$value,$indent) { + // do some folding here, for blocks + if (strpos($value,"\n")) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + } + + $spaces = str_repeat(' ',$indent); + + if (is_int($key)) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + // It's mapped + $string = $spaces.$key.': '.$value."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + function _doLiteralBlock($value,$indent) { + $exploded = explode("\n",$value); + $newValue = '|'; + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $newValue .= "\n" . $spaces . trim($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + if ($this->_dumpWordWrap === 0) { + return $value; + } + + if (strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } + return $value; + } + + /* Methods used in loading */ + + /** + * Finds and returns the indentation of a YAML line + * @access private + * @return int + * @param string $line A line from the YAML file + */ + function _getIndent($line) { + preg_match('/^\s{1,}/',$line,$match); + if (!empty($match[0])) { + $indent = substr_count($match[0],' '); + } else { + $indent = 0; + } + return $indent; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + function _parseLine($line) { + $line = trim($line); + + $array = array(); + + if (preg_match('/^-(.*):$/',$line)) { + // It's a mapped sequence + $key = trim(substr(substr($line,1),0,-1)); + $array[$key] = ''; + } elseif ($line[0] == '-' && substr($line,0,3) != '---') { + // It's a list item but not a new stream + if (strlen($line) > 1) { + $value = trim(substr($line,1)); + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + $array[] = $value; + } else { + $array[] = array(); + } + } elseif (preg_match('/^(.+):/',$line,$key)) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(':',$line); + $key = trim($explode[0]); + array_shift($explode); + $value = trim(implode(':',$explode)); + } + + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if (empty($key)) { + $array[] = $value; + } else { + $array[$key] = $value; + } + } + return $array; + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + function _toType($value) { + if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) { + $value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches)); + $value = preg_replace('/\\\\"/','"',$value); + } elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) { + // Inline Sequence + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + } elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) { + // It's a map + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + $value = array($key => $value); + } elseif (preg_match("/{(.+)}$/",$value,$matches)) { + // Inline Mapping + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $array = array(); + foreach ($explode as $v) { + $array = $array + $this->_toType($v); + } + $value = $array; + } elseif (strtolower($value) == 'null' or $value == '' or $value == '~') { + $value = NULL; + } elseif (ctype_digit($value)) { + $value = (int)$value; + } elseif (in_array(strtolower($value), + array('true', 'on', '+', 'yes', 'y'))) { + $value = TRUE; + } elseif (in_array(strtolower($value), + array('false', 'off', '-', 'no', 'n'))) { + $value = FALSE; + } elseif (is_numeric($value)) { + $value = (float)$value; + } else { + // Just a normal string, right? + $value = trim(preg_replace('/#(.+)$/','',$value)); + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $strings = $strings[2]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + // Check for sequences + if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) { + $inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline); + $seqs = $seqs[0]; + } + + // Check for mappings + if (preg_match_all('/{(.+)}/U',$inline,$maps)) { + $inline = preg_replace('/{(.+)}/U','YAMLMap',$inline); + $maps = $maps[0]; + } + + $explode = explode(', ',$inline); + + // Re-add the strings + if (!empty($strings)) { + $i = 0; + foreach ($explode as $key => $value) { + if ($value == 'YAMLString') { + $explode[$key] = $strings[$i]; + ++$i; + } + } + } + + // Re-add the sequences + if (!empty($seqs)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value); + ++$i; + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + $explode[$key] = str_replace('YAMLMap',$maps[$i],$value); + ++$i; + } + } + } + + return $explode; + } + + /** + * Builds the PHP array from all the YAML nodes we've gathered + * @access private + * @return array + */ + function _buildArray() { + $trunk = array(); + + if (!isset($this->_indentSort[0])) { + return $trunk; + } + + foreach ($this->_indentSort[0] as $n) { + if (empty($n->parent)) { + $this->_nodeArrayizeData($n); + // Check for references and copy the needed data to complete them. + $this->_makeReferences($n); + // Merge our data with the big array we're building + $trunk = $this->_array_kmerge($trunk,$n->data); + } + } + + return $trunk; + } + + /** + * Traverses node-space and sets references (& and *) accordingly + * @access private + * @return bool + */ + function _linkReferences() { + if (is_array($this->_haveRefs)) { + foreach ($this->_haveRefs as $node) { + if (!empty($node->data)) { + $key = key($node->data); + // If it's an array, don't check. + if (is_array($node->data[$key])) { + foreach ($node->data[$key] as $k => $v) { + $this->_linkRef($node,$key,$k,$v); + } + } else { + $this->_linkRef($node,$key); + } + } + } + } + return true; + } + + function _linkRef(&$n,$key,$k = NULL,$v = NULL) { + if (empty($k) && empty($v)) { + // Look for &refs + if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key] = + substr($n->data[$key],strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } elseif (!empty($k) && !empty($v)) { + if (preg_match('/^&([^ ]+)/',$v,$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key][$k] = + substr($v,strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } + } + + /** + * Finds the children of a node and aids in the building of the PHP array + * @access private + * @param int $nid The id of the node whose children we're gathering + * @return array + */ + function _gatherChildren($nid) { + $return = array(); + $node =& $this->_allNodes[$nid]; + foreach ($this->_allNodes as $z) { + if ($z->parent == $node->id) { + // We found a child + $this->_nodeArrayizeData($z); + // Check for references + $this->_makeReferences($z); + // Merge with the big array we're returning + // The big array being all the data of the children of our parent node + $return = $this->_array_kmerge($return,$z->data); + } + } + return $return; + } + + /** + * Turns a node's data and its children's data into a PHP array + * + * @access private + * @param array $node The node which you want to arrayize + * @return boolean + */ + function _nodeArrayizeData(&$node) { + if (is_array($node->data) && $node->children == true) { + // This node has children, so we need to find them + $childs = $this->_gatherChildren($node->id); + // We've gathered all our children's data and are ready to use it + $key = key($node->data); + $key = empty($key) ? 0 : $key; + // If it's an array, add to it of course + if (is_array($node->data[$key])) { + $node->data[$key] = $this->_array_kmerge($node->data[$key],$childs); + } else { + $node->data[$key] = $childs; + } + } elseif (!is_array($node->data) && $node->children == true) { + // Same as above, find the children of this node + $childs = $this->_gatherChildren($node->id); + $node->data = array(); + $node->data[] = $childs; + } + + // We edited $node by reference, so just return true + return true; + } + + /** + * Traverses node-space and copies references to / from this object. + * @access private + * @param object $z A node whose references we wish to make real + * @return bool + */ + function _makeReferences(&$z) { + // It is a reference + if (isset($z->ref)) { + $key = key($z->data); + // Copy the data to this object for easy retrieval later + $this->ref[$z->ref] =& $z->data[$key]; + // It has a reference + } elseif (isset($z->refKey)) { + if (isset($this->ref[$z->refKey])) { + $key = key($z->data); + // Copy the data from this object to make the node a real reference + $z->data[$key] =& $this->ref[$z->refKey]; + } + } + return true; + } + + + /** + * Merges arrays and maintains numeric keys. + * + * An ever-so-slightly modified version of the array_kmerge() function posted + * to php.net by mail at nospam dot iaindooley dot com on 2004-04-08. + * + * http://us3.php.net/manual/en/function.array-merge.php#41394 + * + * @access private + * @param array $arr1 + * @param array $arr2 + * @return array + */ + function _array_kmerge($arr1,$arr2) { + if(!is_array($arr1)) + $arr1 = array(); + + if(!is_array($arr2)) + $arr2 = array(); + + $keys1 = array_keys($arr1); + $keys2 = array_keys($arr2); + $keys = array_merge($keys1,$keys2); + $vals1 = array_values($arr1); + $vals2 = array_values($arr2); + $vals = array_merge($vals1,$vals2); + $ret = array(); + + foreach($keys as $key) { + list($unused,$val) = each($vals); + // This is the good part! If a key already exists, but it's part of a + // sequence (an int), just keep addin numbers until we find a fresh one. + if (isset($ret[$key]) and is_int($key)) { + while (array_key_exists($key, $ret)) { + $key++; + } + } + $ret[$key] = $val; + } + + return $ret; + } + } +?> \ No newline at end of file diff --git a/library/YAML/spyc.yml b/library/YAML/spyc.yml new file mode 100755 index 0000000..32d1459 --- /dev/null +++ b/library/YAML/spyc.yml @@ -0,0 +1,106 @@ +# +# S P Y C +# a simple php yaml class +# v0.2(.2) +# +# author: [chris wanstrath, chris@ozmm.org] +# websites: [http://www.yaml.org, http://spyc.sourceforge.net/] +# license: [MIT License, http://www.opensource.org/licenses/mit-license.php] +# copyright: (c) 2005-2006 Chris Wanstrath +# +# spyc.yml - A file containing the YAML that Spyc understands. + +# Added in .2 +1040: Ooo, a numeric key! # And working comments? Wow! + +# Mappings - with proper types +String: Anyone's name, really. +Int: 13 +True: true +False: false +Zero: 0 +Null: NULL +Float: 5.34 + +# A sequence +- PHP Class +- Basic YAML Loader +- Very Basic YAML Dumper + +# A sequence of a sequence +- + - YAML is so easy to learn. + - Your config files will never be the same. + +# Sequence of mappings +- + cpu: 1.5ghz + ram: 1 gig + os : os x 10.4.1 + +# Mapped sequence +domains: + - yaml.org + - php.net + +# A sequence like this. +- program: Adium + platform: OS X + type: Chat Client + +# A folded block as a mapped value +no time: > + There isn't any time + for your tricks! + + Do you understand? + +# A literal block as a mapped value +some time: | + There is nothing but time + for your tricks. + +# Crazy combinations +databases: + - name: spartan + notes: + - Needs to be backed up + - Needs to be normalized + type: mysql + +# You can be a bit tricky +"if: you'd": like + +# Inline sequences +- [One, Two, Three, Four] + +# Nested Inline Sequences +- [One, [Two, And, Three], Four, Five] + +# Nested Nested Inline Sequences +- [This, [Is, Getting, [Ridiculous, Guys]], Seriously, [Show, Mercy]] + +# Inline mappings +- {name: chris, age: young, brand: lucky strike} + +# Nested inline mappings +- {name: mark, age: older than chris, brand: [marlboro, lucky strike]} + +# References -- they're shaky, but functional +dynamic languages: &DLANGS + - Perl + - Python + - PHP + - Ruby +compiled languages: &CLANGS + - C/C++ + - Java +all languages: + - *DLANGS + - *CLANGS + +# Added in .2.2: Escaped quotes +- you know, this shouldn't work. but it does. +- 'that''s my value.' +- 'again, that\'s my value.' +- "here's to \"quotes\", boss." \ No newline at end of file diff --git a/library/YAML/test.php b/library/YAML/test.php new file mode 100755 index 0000000..b6473b2 --- /dev/null +++ b/library/YAML/test.php @@ -0,0 +1,152 @@ + "1.5ghz", "ram" => "1 gig", + "os" => "os x 10.4.1")) + die('Sequence 4 failed'); + +# Mapped sequence +if ($yaml['domains'] != array("yaml.org", "php.net")) + die("Key: 'domains' failed"); + +# A sequence like this. +if ($yaml[5] != array("program" => "Adium", "platform" => "OS X", + "type" => "Chat Client")) + die('Sequence 5 failed'); + +# A folded block as a mapped value +if ($yaml['no time'] != "There isn't any time for your tricks! \nDo you understand?") + die("Key: 'no time' failed"); + +# A literal block as a mapped value +if ($yaml['some time'] != "There is nothing but time\nfor your tricks.") + die("Key: 'some time' failed"); + +# Crazy combinations +if ($yaml['databases'] != array( array("name" => "spartan", "notes" => + array( "Needs to be backed up", + "Needs to be normalized" ), + "type" => "mysql" ))) + die("Key: 'databases' failed"); + +# You can be a bit tricky +if ($yaml["if: you'd"] != "like") + die("Key: 'if: you\'d' failed"); + +# Inline sequences +if ($yaml[6] != array("One", "Two", "Three", "Four")) + die("Sequence 6 failed"); + +# Nested Inline Sequences +if ($yaml[7] != array("One", array("Two", "And", "Three"), "Four", "Five")) + die("Sequence 7 failed"); + +# Nested Nested Inline Sequences +if ($yaml[8] != array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), + "Seriously", array("Show", "Mercy"))) + die("Sequence 8 failed"); + +# Inline mappings +if ($yaml[9] != array("name" => "chris", "age" => "young", "brand" => "lucky strike")) + die("Sequence 9 failed"); + +# Nested inline mappings +if ($yaml[10] != array("name" => "mark", "age" => "older than chris", + "brand" => array("marlboro", "lucky strike"))) + die("Sequence 10 failed"); + +# References -- they're shaky, but functional +if ($yaml['dynamic languages'] != array('Perl', 'Python', 'PHP', 'Ruby')) + die("Key: 'dynamic languages' failed"); + +if ($yaml['compiled languages'] != array('C/C++', 'Java')) + die("Key: 'compiled languages' failed"); + +if ($yaml['all languages'] != array( + array('Perl', 'Python', 'PHP', 'Ruby'), + array('C/C++', 'Java') + )) + die("Key: 'all languages' failed"); + +# Added in .2.2: Escaped quotes +if ($yaml[11] != "you know, this shouldn't work. but it does.") + die("Sequence 11 failed."); + +if ($yaml[12] != "that's my value.") + die("Sequence 12 failed."); + +if ($yaml[13] != "again, that's my value.") + die("Sequence 13 failed."); + +if ($yaml[14] != "here's to \"quotes\", boss.") + die("Sequence 14 failed."); + + +print "spyc.yml parsed correctly\n"; + +?> \ No newline at end of file diff --git a/library/YAML/yaml-dump.php b/library/YAML/yaml-dump.php new file mode 100755 index 0000000..8a343de --- /dev/null +++ b/library/YAML/yaml-dump.php @@ -0,0 +1,36 @@ + 'A sequence','second' => 'of mapped values'); +$array['Mapped'] = array('A sequence','which is mapped'); +$array['A Note'] = 'What if your text is too long?'; +$array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.'; +$array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.'; +$array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!"; + +$yaml = Spyc::YAMLDump($array,4,60); + +echo '
    A PHP array run through YAMLDump():
    '; +print_r($yaml); +echo '
    '; + +?> \ No newline at end of file diff --git a/library/YAML/yaml-load.php b/library/YAML/yaml-load.php new file mode 100755 index 0000000..88a149a --- /dev/null +++ b/library/YAML/yaml-load.php @@ -0,0 +1,23 @@ +spyc.yml loaded into PHP:
    '; +print_r($array); +echo '
    '; + + +?> \ No newline at end of file diff --git a/library/adapters/mysql.php b/library/adapters/mysql.php new file mode 100755 index 0000000..9f17c07 --- /dev/null +++ b/library/adapters/mysql.php @@ -0,0 +1,132 @@ + + // @created 2005-09-28 + // @desc Used to connect and interact with MySQL databases. Inherits + // basic functionality and structure from the DBAbstract class. + + include_once 'library/database.php'; + include_once 'library/stdexception.php'; + + class mysql extends database { + // $config, $handle, $is_connected; // state and configuration // defined in 'database' class + + // query templates + const select = 'SELECT :cache :columns FROM :table :join :where :group_by :having :order_by :limit :offset;'; + const delete = 'DELETE FROM :table :where :order_by :limit;'; + const insert = 'INSERT INTO :table SET :values;'; + const update = 'UPDATE :table SET :values :where :order_by :limit;'; + // transactional statements + const begin = 'BEGIN;'; + const commit = 'COMMIT;'; + const rollback = 'ROLLBACK;'; + // query templates' parts + const cache = 'SQL_CACHE'; + const join = 'LEFT JOIN :table ON :on :join'; + const where = 'WHERE :where'; + const group_by = 'GROUP BY :group_by'; + const having = 'HAVING :having'; + const order_by = 'ORDER BY :order_by'; + const limit = 'LIMIT :limit'; + const offset = 'OFFSET :offset'; + // meta templates + const named_entity = ':name AS :alias'; + const equality = '"{:1}" = "{:2}"'; + const value = '{:1} = "{:2}"'; + + // constructor // there's magic working here, so don't overwrite what's already in here unless + // you are willing to clean up after yourself + public function __construct($params) { + parent::__construct($params); + parent::$adapter_class = get_class(); + } + + // actions + public function connect() { + // establish connection + if(!($this->handle = @mysql_pconnect($this->config['host'], $this->config['username'], $this->config['password']))) + throw new DBConnectionException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + parent::$static_handle = $this->handle; + $this->is_connected = true; + return true; + } + + public function disconnect() { + if(mysql_close($this->db)) { + $this->is_connected = false; + $this->db = null; + return true; + } else { + return false; + } + } + + public function select_db($database = null) { + if(!empty($database)) $this->config['database'] = $database; else $database = $this->config['database']; + if($this->is_connected) { + if(!@mysql_select_db($database, $this->handle)) throw new CouldNotSelectDB(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return true; + } else { + throw new CouldNotSelectDB("0002", "Not connected to the Database", (__FILE__ . ', line ' . __LINE__)); + } + } + + protected function query($query) { + @mysql_free_result($this->result); + + Debug::log("Executed '{$query}'", 'sql', 'info', 'adapter::MySQL'); + +// try { +// if(!($this->result = @mysql_query($query, $this->handle))) throw new DBQueryException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); +// } catch(Exception $e) { +// print_r($e); die(); +// } + + // execute the query and return the result + $this->result = @mysql_query($query, $this->handle); + + return $this->result; + } + + public function iterate($result) { + // iterate through the results + return @mysql_fetch_assoc($result); + } + + // functions + public function rows_found() { + return mysql_num_rows($this->result); + } + public function affected_rows() { + return mysql_affected_rows(); + } + public function last_id() { + return mysql_insert_id(); + } + // alias + public function id() { + return $this->last_id(); + } + + // sanitize values + public static function sanitize($value) { + return addslashes(stripslashes($value)); + } + + // get table column names (to make sure 'updates' and 'inserts' don't try to update a column that doesn't exist) + public static function columns($table) { + // query + $query = "EXPLAIN {$table};"; + + // get table details + $result = @mysql_query($query, parent::$static_handle); + + // loop through details + while($row = @mysql_fetch_assoc($result)) { + $columns[] = $row['Field']; + } + + return $columns; + } + } +?> \ No newline at end of file diff --git a/library/adapters/mysql_backup.php b/library/adapters/mysql_backup.php new file mode 100755 index 0000000..b7d8fa2 --- /dev/null +++ b/library/adapters/mysql_backup.php @@ -0,0 +1,320 @@ + + // @created 2005-09-28 + // @desc Used to connect and interact with MySQL databases. Inherits + // basic functionality and structure from the DBAbstract class. + +// include_once 'db.php'; +// include_once 'stdexception.php'; + + class mysql extends database { + // defined in the 'database' class + // public $config, $handle, $query, $result, $is_connected; // state and configuration + + // functions to override +/* abstract public function connect(); + abstract public function disconnect(); + abstract public function select_db($database); + abstract public function query($query); + abstract public function iterate(); + abstract public function select($params, $table); +*/ + + // overridden functions + public function connect() { + // establish connection + if(!($this->handle = @mysql_pconnect($this->config['host'], $this->config['username'], $this->config['password']))) die(mysql_error()); // throw new DBConnectionException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + $this->is_connected = true; + return true; + } + + public function disconnect() { + if(mysql_close($this->db)) { + $this->is_connected = false; + $this->db = null; + return true; + } else { + return false; + } + } + + public function select_db($database = null) { + if(!empty($database)) $this->config['database'] = $database; else $database = $this->config['database']; + if($this->is_connected) { + if(!@mysql_select_db($database, $this->handle)) throw new CouldNotSelectDB(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return true; + } else { + throw new CouldNotSelectDB("0002", "Not connected to the Database", (__FILE__ . ', line ' . __LINE__)); + } + } + + public function query($query) { + $this->query = $query; + // print $query . "
    \n"; + + @mysql_free_result($this->result); + + Debug::log("Executed '{$this->query}'", 'sql', 'info', 'MySQL'); + +// try { + if(!($this->result = @mysql_query($this->query, $this->db))) throw new CouldNotIterate(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return true; +// } +// catch(DBException $e) { +// // $this->result = null; +// print_r($e); +// $this->error = $e; +// return false; +// } + } + + public function get_row() { + try { + switch($this->row_result_type) { + case "self::ROWRESTYPE_INDEXED": + case self::ROWRESTYPE_INDEXED: + if(!($this->row = @mysql_fetch_row($this->result))) throw new DBException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return $this->row; + break; + default: + case "self::ROWRESTYPE_ASSOC": + case self::ROWRESTYPE_ASSOC: + if(!($this->row = @mysql_fetch_assoc($this->result))) throw new DBException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return $this->row; + break; + case "self::ROWRESTYPE_OBJECT": + case self::ROWRESTYPE_OBJECT: + if(!($this->row = @mysql_fetch_object($this->result))) throw new DBException(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + return $this->row; + break; + } + } + catch(DBException $e) { + print_r($e); + $this->row = null; + $this->error = $e; + return false; + } + } + + public function iterate($result = null) { + if($result == null) $result = $this->result; +// try { + if(!($this->row = @mysql_fetch_assoc($result)) && $this->row != false) throw new CouldNotIterate(mysql_errno(), mysql_error(), (__FILE__ . ', line ' . __LINE__)); + // print_r($this->row); + return $this->row; +// } +// catch(DBException $e) { +// print_r($e); +// $this->row = null; +// $this->error = $e; +// return false; +// } + } + public function select($params, $table) { + // where operators + $where_operator = !empty($params["where_operator"]) ? $params["where_operator"] : "AND"; + + // compile list of columns to select (usually defaults to "*") + if(is_array($params["columns"])) { + $columns = implode($params["columns"], ", "); + } else { + // if nothing is specified, select everything + $columns = !empty($params["columns"]) ? $params["columns"] : "*"; + } + + // compile the list of WHERE arguments + if(is_array($params["where"])) { + foreach($params["where"] as $column=>$values) { + if(is_array($values) && is_int($column)) { + // support for passing in arrays of ('key','value') + if($where != "") $where .= " {$where_operator} "; // append value of $strWhereOperator + $where .= "{$values[0]}='{$values[1]}'"; + } elseif(is_array($values)) { + foreach($values as $value) { + if($clause != "") $clause .= " OR "; + $clause .= "{$column}='{$value}'"; + } + if($where != "") $where .= " {$where_operator} "; // append value of $strWhereOperator + $where .= $clause; + } else { + if($where != "") $where .= " {$where_operator} "; // append value of $strWhereOperator + $where .= "{$column}='{$values}'"; + } + } + } else { + if(!empty($params["where"])) $where = $params["where"]; + } + if(!empty($where)) $where = "WHERE {$where}"; + + // order by + if(!empty($params["order_by"])) $order_by = "ORDER BY " . $params["order_by"]; + + // limit + if(!empty($params["limit"])) $limit = "LIMIT " . $params["limit"]; + + // "SELECT {$columns} FROM {$table} {$strWhere}{$strOrderBy};" + $query = 'SELECT %s FROM %s %s %s %s;'; + + // debug purposes + // print sprintf($query, $columns, $table, $where, $order_by, $limit) . "
    \n"; + + // execute query + $this->query(sprintf($query, $columns, $table, $where, $order_by, $limit)); + } + public function insert($params, $table) { + // get columns of the table + $table_columns = $this->explain_table($table); + + // compile list of columns and values to insert + foreach($params as $column=>$value) { + // check if it's an association + if(is_array($value) || is_object($value)) continue; + + // don't insert 'id' values + if($column == "id") continue; + + // also, don't update if it's not one of the columns in the table (uh, duh) + if(!in_array($column, $table_columns)) continue; + + // add commas + if(!empty($columns)) $columns .= ', '; + if(!empty($values)) $values .= ', '; + + // add slashes to help prevent SQL Injection problems + // $value = addslashes($value); + + // assemble query strings + $columns .= "{$column}"; + $values .= "'{$value}'"; + } + + // "INSERT INTO {$table} ({$strColumns}) VALUES ({$values});" + $query = 'INSERT INTO %s (%s) VALUES (%s);'; + + // debug purposes + // print sprintf($query, $table, $columns, $values); + + // execute query + $this->query(sprintf($query, $table, $columns, $values)); + } + public function update($params, $table) { + // get columns of the table + $table_columns = $this->explain_table($table); + + // compile list of columns and values to insert + foreach($params as $column=>$value) { + // check if it's an association + if(is_array($value) || is_object($value)) continue; + + // don't update 'id' values (in the where clause) + if($column == "id") continue; + + // also, don't update if it's not one of the columns in the table (uh, duh) + if(!in_array($column, $table_columns)) continue; + + // add commas + if(!empty($columns)) $columns .= ', '; + + // add slashes to help prevent SQL Injection problems + // $value = addslashes($value); + + // assemble query strings + $columns .= "{$column}='{$value}'"; + } + + // "UPDATE {$table} SET {$columns} WHERE id='{$params[id]}';" + $query = 'UPDATE %s SET %s WHERE id=\'%s\';'; + + // debug purposes + // print sprintf($query, $table, $columns, $params['id']); + + // execute query + $this->query(sprintf($query, $table, $columns, $params['id'])); + } + public function delete($params, $table) { + // get params + $id = empty($params['id']) ? $params['where']['id'] : $params['id']; + + // "DELETE FROM {$table} WHERE id='{$nId}';" + $query = 'DELETE FROM %s WHERE id=\'%s\' LIMIT 1;'; + + // execute query + $this->query(sprintf($query, $table, $id)); + // print "DELETE FROM {$table} WHERE id='{$nId}';"; + } + public function delete_all($params, $table) { + // where operators + $where_operator = !empty($params["where_operator"]) ? $params["where_operator"] : "AND"; + + // compile list of columns to select (usually defaults to "*") + if(is_array($params["columns"])) { + $columns = implode($params["columns"], ", "); + } else { + // if nothing is specified, select everything + $columns = !empty($params["columns"]) ? $params["columns"] : "*"; + } + + // compile the list of WHERE arguments + if(is_array($params["where"])) { + foreach($params["where"] as $column=>$value) { + if($where != "") $where .= " {$where_operator} "; // append value of $strWhereOperator + $where .= "{$column}='{$value}'"; + } + } else { + if(!empty($params["where"])) $where = $params["where"]; + } + if(!empty($where)) $where = "WHERE {$where}"; + + if(!empty($params['limit'])) $limit = "LIMIT {$params[limit]}"; // for when in 'delete' // else $limit = "LIMIT 1"; + + // "SELECT {$columns} FROM {$table} {$strWhere}{$strOrderBy};" + $query = 'DELETE FROM %s %s %s;'; + + // debug purposes + // print sprintf($query, $table, $where, $limit) . "
    \n"; + + // execute query + $this->query(sprintf($query, $table, $where, $limit)); + } + + // functions + public function get_last_id() { + return mysql_insert_id(); + } + public function rows_found() { + return mysql_num_rows($this->result); + } + public function affected_rows() { + return mysql_affected_rows($this->db); + } + + // get table column names (to make sure 'updates' and 'inserts' don't try to update a column that doesn't exist) + public function explain_table($table) { + // query + $query = "EXPLAIN {$table};"; + + // get table details + $result = @mysql_query($query, $this->db); + + // loop through details + while($row = @mysql_fetch_assoc($result)) { + $rows[] = $row; + } + + foreach($rows as $column) { + $columns[] = $column['Field']; + } + + return $columns; + } + } + + class CouldNotConnect extends StdException { } + class CouldNotSelectDB extends StdException { } + class CouldNotIterate extends StdException { } + class CouldNotFind extends StdException { } + class CouldNotSave extends StdException { } + class CouldNotDelete extends StdException { } +?> \ No newline at end of file diff --git a/library/database.php b/library/database.php new file mode 100755 index 0000000..d1d712d --- /dev/null +++ b/library/database.php @@ -0,0 +1,409 @@ + + // @created 2005-09-26 + // @desc An abstract class to standardize the database interaction methods + // (to be implemented/inherited from a specific class, such as 'MySQL', + // 'MSSQL', et al). + // @requires stdexception.php (StdException class) + + include_once 'stdexception.php'; + + // classes + abstract class database { + // protected variables + public $config, $handle, $result, $is_connected; // state and configuration + + // adapter type + public static $adapter_class = ''; // set automatically in the constructor + public static $static_handle = null; // static handle to the DB + + /* + @ Adapters need to define these constants for the Query Builder + + // query templates + const select = 'SELECT :columns FROM :table :join :where :group_by :having :order_by :limit'; + const delete = 'DELETE FROM :table :where :order_by :limit;'; + const insert = 'INSERT INTO :table SET :values;'; + const update = 'UPDATE :table SET :values :where :order_by :limit;'; + // query templates' parts + const join = 'LEFT JOIN :table ON :on :join'; + const where = 'WHERE :where'; + const group_by = 'GROUP BY :group_by'; + const having = 'HAVING :having'; + const order_by = 'ORDER BY :order_by'; + const limit = 'LIMIT :limit :offset'; + const offset = 'OFFSET :offset'; + // meta templates + const named_entity = ':name AS :alias'; + const equality = '":1" = ":2"'; + */ + + // constructor + public function __construct($params) { + // foreach($params as $key=>$value) $this->config[$key] = $value; // '$this->config = $params;' is much shorter and far more optimized + $this->config = $params; + + // defaults + $this->handle = null; + self::$static_handle = null; + $this->is_connected = false; + } + + // abstract functions + abstract public function connect(); + abstract public function select_db($database = null); + abstract public function disconnect(); + abstract protected function query($params); + abstract public function iterate($result); + + // interface functions + public function find($params) { + // if $params is null, that means we should select everything! + // or, if it's a string, select whichever is specified: first, last, all, et al + + // select all columns by default + if(empty($params['columns'])) $params['columns'] = '*'; + + if(is_array($params)) { + // minimal preparations needed to be done to execute select query + } elseif(is_string($params)) { + // depending on string, set certain parameters + switch(strtolower($params)) { + case 'all': break; + case 'first': $params['limit'] = '1'; + break; + case 'last': $params['order_by'] = 'id DESC'; + $params['limit'] = '1'; + break; + } + $params = self::prepare_params($params); + } else { + $params = self::prepare_params($params); + } + + $result = $this->select($params); + + // throw an exception if there's a problem + // why throw an exception? // if($result == false) throw new CouldNotFind('Could not find anything'); + + while($row = $this->iterate($result)) { + $rows[$row['id']] = new row($row); + } + + return $rows; + } + public function save($params) { + $row = $params['values']; + $params['values'] = self::build_values($params['values']->as_array()); + + // if the ID is set, update the row; otherwise, insert a new one + if($row->id) + $result = $this->update($params); + else + $result = $this->insert($params); + + // throw an exception if there's a problem + if($result == false) throw new CouldNotSave('Could not save the data'); + + // get the ID set properly if it isn't set (for new entries) + if(!$row->id) $row->id = $this->id($result); + + // return the row saved + return $row; + } + + // action functions + protected function select($params) { + $params[0] = self::get_clause('select'); + + // build special clauses + $params['columns'] = self::build_columns($params); + $params = self::build_join($params); + if(empty($params['where'][0]) && !empty($params['where'])) $params['where'][0] = self::build_where_query($params['where']); + + // build other clauses + $clauses = array('cache', 'where', 'order_by', 'group_by', 'limit', 'offset', 'having'); + foreach($clauses as $clause) { + if(!empty($params[$clause])) $params[$clause] = self::build_clause($clause, $params[$clause]); + } + + return $this->query(self::build_query(self::prepare_params($params))); + } + protected function insert($params) { + $params[0] = self::get_clause('insert'); + + return $this->query(self::build_query(self::prepare_params($params))); + } + protected function update($params) { + $params[0] = self::get_clause('update'); + + // build other clauses + $clauses = array('where', 'order_by', 'limit'); + foreach($clauses as $clause) { + if(!empty($params[$clause])) $params[$clause] = self::build_clause($clause, $params[$clause]); + } + + return $this->query(self::build_query(self::prepare_params($params))); + } + public function delete($params) { + $params[0] = self::get_clause('delete'); + + // build where clause + if(empty($params['where'][0]) && !empty($params['where'])) $params['where'][0] = self::build_where_query($params['where']); + + // build other clauses + $clauses = array('where', 'order_by', 'group_by', 'limit', 'offset', 'having'); + foreach($clauses as $clause) { + if(!empty($params[$clause])) $params[$clause] = self::build_clause($clause, $params[$clause]); + } + + $result = $this->query(self::build_query(self::prepare_params($params))); + + // throw an exception if there's a problem + if($result == false) throw new CouldNotDelete('Could not delete the row'); + + return $result; + } + + // transactional functionality + protected function begin() { + $params[0] = self::get_clause('begin'); + return $this->query(self::build_query(self::prepare_params($params))); + } + protected function commit() { + $params[0] = self::get_clause('commit'); + return $this->query(self::build_query(self::prepare_params($params))); + } + protected function end() { + // alias of commit() + return $this->commit(); + } + protected function rollback() { + $params[0] = self::get_clause('rollback'); + return $this->query(self::build_query(self::prepare_params($params))); + } + + // internal functions (builders, dependent builders, special builders, et al) + protected static function build_query($params) { + $query = $params[0]; + unset($params[0]); + + $query = self::build_sub_query($query, $params); + + // handle root references + foreach($params as $key=>$value) { + $query = str_replace(":@{$key}", $value, $query); + } + + return $query; + } + protected static function build_sub_query($query, $params) { + foreach($params as $key=>$value) { + // if it's an array, it needs to be built itself and then the contents can be directly subbed in + if(is_array($value)) + $value = self::build_sub_query($query, $value); + + if(empty($value)) + $query = str_replace(" :{$key}", '', $query); + else { + $query = str_replace(":{$key}", $value, $query); + } + } + + // handle backreferences and parent references + $query = self::build_query_backreferences($query, $params); + + return $query; + } + protected static function build_query_backreferences($query, $params) { + foreach($params as $key=>$value) { + $query = str_replace(":{$key}", $value, $query); + } + return preg_replace('/:#(#*)([\w\d_]+)/', ':$1$2', $query); + } + protected static function prepare_params($params) { + // the clauses to check against + $clauses = self::get_clauses_from($params[0]); + + // make sure that all values are set and the proper clauses are set + foreach($clauses as $clause) { + if(empty($params[$clause])) { $params[$clause] = ''; continue; } // if it's not been set, give it an empty value + // if(is_array($params[$clause]) && empty($params[0])) $params[$clause][0] = self::get_clause($clause); // otherwise, get its clause + } + + return $params; + } + + // special dependent builder functions + protected static function build_values($params) { + foreach($params as $column=>$value) { + if($column == 'id') continue; + $value = self::sanitize($value); + $values .= (((!empty($values)) ? ', ' : '') . str_replace('{:1}', $column, str_replace('{:2}', $value, self::get_clause('value')))); + } + return $values; + } + protected static function build_where_query($params) { + foreach($params as $column=>$value) { + $query .= (((!empty($query)) ? ' and ' : '') . str_replace('{:1}', $column, str_replace('{:2}', ":{$column}", self::get_clause('value')))); + } + return $query; + } + protected static function build_clause($name, $clause_data) { + $clause_template = self::get_clause($name); + if(empty($clause_template)) $clause_template = ":{$name}"; + if(is_array($clause_data)) { + $clause_form = $clause_data[0]; + unset($clause_data[0]); + foreach($clause_data as $key=>$value) { + $clause_form = str_replace(":{$key}", $value, $clause_form); + } + $clause_data = $clause_form; + } + return str_replace(":{$name}", $clause_data, $clause_template);; + } + protected static function get_clause($name) { + $class = new ReflectionClass(self::$adapter_class); + $constants = $class->getConstants(); + return $constants[$name]; + } + protected static function get_clauses_from($query) { + $query = explode(' ', $query); + + foreach($query as $clause) { + if($clause[0] != ':') continue; + $clauses[] = substr($clause, 1); + } + + return $clauses; + } + + // special, overridable builders + protected static function build_columns($params) { + if(!is_array($params['columns'])) return $params['columns']; + + $columns = $params['columns']; + foreach($columns as $column) { + $columns_as_string = ((!empty($columns_as_string)) ? ', ' : '') . "{$params['table']}.*"; + } + + return $columns; + } + protected static function build_join($params) { + // if(empty($params['join'][0])) $params['join'][0] = self::get_clause('join'); + // if(!empty($params['join']['join'])) $params['join']['join'] = self::build_join($params['join']); + + if(empty($params['join'])) return $params; + + // handle columns + if(empty($params['columns']) || $params['columns'] == '*') $params['columns'] = array("{$params['table']}.*"); + foreach(self::get_join_columns($params['join']) as $column) { + $params['columns'][] = $column; + } + $params['columns'] = implode(', ', $params['columns']); + + // assemble join clauses + $params['join'] = self::add_join_clause($params['join']); + + return $params; + } + protected static function add_join_clause($join) { + $query = $join[0]; + unset($join[0]); + + // get sub-join query clause if there is one available + if(empty($query)) $query = self::get_clause('join'); + + // get the table name as singular form + $join['singular_table'] = Inflector::singularize($join['table']); + + if(!empty($join['join'])) + $join['join'] = self::add_join_clause($join['join']); + else + $query = str_replace(' :join', '', str_replace(' :#join', '', $query)); + + $query = self::build_sub_query($query, $join); + + return $query; + } + protected static function get_join_columns($join) { + // get nested joins' columns + if(!empty($join['join'])) + $columns = self::get_join_columns($join['join']); + + if(empty($join['columns'])) $join['columns'] = self::get_columns($join['table']); + + // get columns + if(!is_array($join['columns'])) $join['columns'] = explode(',', str_replace(' ', '', $join['columns'])); + foreach($join['columns'] as $column) { + // skip over 'id' columns (because 'id' is gotten for the parent table) + if($column == 'id') continue; + // associate table and columns + $columns[] = "{$join['table']}.{$column}" . ($column == '*' ? '' : " AS {$column}"); + } + + return $columns; + } + + // support functions + protected static function get_columns($table) { + return call_user_func(array(self::$adapter_class, 'columns'), $table); + } + + protected static function sanitize($value) { + return call_user_func(array(self::$adapter_class, 'sanitize'), $value); + } + + // descructor + public function __destruct() { + // possibly put some serialization code in here? + // end; + } + } + + // as soon as Row is not used, get rid of it + class row { + // variables + private $row; + + // constructor + public function __construct($row = array()) { + $this->row = $row; + } + + // magic function override + public function __set($name, $value) { + $this->row[$name] = $value; + } + public function __get($name) { + return $this->row[$name]; + } + + // functions + public function to_array() { + return $this->row; + } + // alias + public function as_array() { + return $this->to_array(); + } + + // remove non columns (to help with associations and joins) + public function remove_non_columns($columns) { + foreach($this->row as $column=>$value) { + if(array_search($column, $columns) === false) unset($this->row[$column]); + } + } + } + + class DBException extends StdException {} + class DBQueryException extends DBException {} + class DBExecutionException extends DBException {} + class CouldNotConnect extends DBException {} + class CouldNotSelectDB extends DBException {} + class CouldNotIterate extends DBException {} + class CouldNotFind extends DBException {} + class CouldNotSave extends DBException {} + class CouldNotDelete extends DBException {} +?> \ No newline at end of file diff --git a/library/dependencies.php b/library/dependencies.php new file mode 100644 index 0000000..7c78402 --- /dev/null +++ b/library/dependencies.php @@ -0,0 +1,62 @@ + + // @created_on 29 Apr 2006 + // @note Any statement followed by a "// *" comment (possibly followed by commentary) is REQUIRED! + + { // required libraries and globals + // load conventions (for paths et al) + include_once $GLOBALS['working_dir'] . "/library/Conventions.php"; // * + + // set app_dir for conventions + Conventions::$app_dir = $GLOBALS['working_dir'] . '/'; // * + + // super-dependencies (that all of the others use) + include_once Conventions::library_path("YAML"); // * // adds YAML file parsing (for config) + + include_once Conventions::library_path("Config2"); // * + include_once Conventions::library_path("AutoLoad"); // * // autoloads models as needed + include_once Conventions::library_path("Globals"); // * + + // settings & config libraries + include_once Conventions::library_path("Debug"); // * // handles debugging + include_once Conventions::library_path("File"); // * + include_once Conventions::library_path("Router2"); // * + + // primary components and libraries + include_once Conventions::library_path("Request"); // * + include_once Conventions::library_path("Response"); // * + include_once Conventions::library_path("Session"); // * + + // mvc components + // @desc You can exclude any of these, but it makes sense to use at least one, such as Model2, + // or Controller. Please read the API for overloading default functionality if you choose + // to use just one (so that Controller isn't dependent on View, for instance). + include_once Conventions::library_path("Controller"); // @depends_on Config2, Conventions, Debug, View + include_once Conventions::library_path("Model2"); // @depends_on Config2, YAML, Conventions, Debug + include_once Conventions::library_path("View"); // @depends_on Smarty (unless overridden) + + // base mvc classes + // @desc These are the default classes for the system: any other classes (Controllers, Views, Helpers) should + // inheret from these base classes, unless you prefer to have no global functionality (such as exception-handling) + include_once Conventions::controller_path('application'); // loads the default controller + include_once Conventions::helper_path('application'); // loads the default helper + // include_once Conventions::view_class_file_path('application'); // loads the default view // unused as of 1.0.4 + + // Smarty libs + include_once Conventions::library_path('smarty/Smarty.class'); // @used_by View + + // standard extensions + include_once Conventions::library_path("ext/Pager"); // handles pagination + include_once Conventions::library_path("ext/Pluralize"); // handles pluralization (used in helpers) + include_once Conventions::extension_path("RedCloth"); // adds specialized formatting for input/output + } + + { // load route mappings + include_once Conventions::config_file('routes'); + } + +?> \ No newline at end of file diff --git a/library/ext/Pager.php b/library/ext/Pager.php new file mode 100755 index 0000000..e02982e --- /dev/null +++ b/library/ext/Pager.php @@ -0,0 +1,117 @@ + + // @created 2006-03-15 + // @requires extexception.php (extends ExtException class) + // @usage $p = new Pager($rows, $per_page); + // $p->page; + + // classes + class Pager { + private $pages = array(); + private $page = null; + + // constructor (vital) + public function __construct($collection, $per_page) { + if(!is_array($collection)) $collection = array(); + $this->pages = array_chunk($collection, $per_page); + } + public static function paginate($collection, $per_page) { + return new Pager($collection, $per_page); + } + + // navigation/iteration functions + public function next() { + if(!next($this->pages)) return false; + return $this; + } + public function previous() { + if(!prev($this->pages)) return false; + return $this; + } + public function first() { + reset($this->pages); + return $this; + } + public function last() { + end($this->pages); + return $this; + } + public function current() { + return current($this->pages); + } + public function all() { + return current($this->pages); + } + public function page($page = null) { + $this->page = $page; + if($page !== null) return $this->pages[$page - 1]; + return current($this->pages); + } + public function all_pages() { + return $this->pages; + } + public function page_numbers() { + for($i = 0; $i < count($this->pages); $i++) { + $page_numbers[] = $i + 1; + } + return $page_numbers; + } + public function page_number() { + if($this->page == null) return 1; + return $this->page; + } + + // testing functions + public function has_next() { + // if the page has been set and the next one is set, then there has to be a next page + if(!empty($this->page)) { + if(!empty($this->pages[($this->page - 1) + 1])) return true; // see note below + else return false; + // NOTE: the page - 1 + 1 is basically + // for semantics because the page value + // is actually one greater than the actual + // page id in the array + } + // skips all this extra complicated crap only if $this->page has been set (by $this->page($page);) + $pages = $this->pages; + self::set_pointer($pages); + if(!next($pages)) return false; + return true; + } + public function has_previous() { + // if the page has been set and it's above 1, then there has to be a previous page + if(!empty($this->page)) { + if(($this->page - 1) > 0) return true; + else return false; + } + // skips all this extra complicated crap only if $this->page has been set (by $this->page($page);) + $pages = $this->pages; + self::set_pointer($pages); + if(!prev($pages)) return false; + return true; + } + private function set_pointer(&$pages) { + foreach($pages as $page) { + $res = array_diff(current($this->pages), $page); + if(empty($res)) break; + } + } + + // magic functions + public function __get($name) { + if($name == 'page') return $this->current(); + if($name == 'next_page') return $this->page + 1; + if($name == 'previous_page') return $this->page - 1; + if($name == 'count' || $name == 'page_count' || $name == 'sizeof') return count($this->collection); + // if($name == '') return $x; // + return false; + } + public function __set($name, $value) { + return false; + } + } + + class PagerException extends StdException {} +?> \ No newline at end of file diff --git a/library/ext/Pluralize.php b/library/ext/Pluralize.php new file mode 100755 index 0000000..f93ea07 --- /dev/null +++ b/library/ext/Pluralize.php @@ -0,0 +1,478 @@ + + // @created 2006-01-08 + // @desc Handles pluralizing words passed to it. + // @refer_to "An Algorithmic Approach to English Pluralization":http://www.csse.monash.edu.au/~damian/papers/HTML/Plurals.html + // @requires stdexception.php (StdException class) + + // inflector (handles pluralization and singularization) + class Inflector { + private static $words_that_do_not_inflect_in_the_plural = array("fish", "-ois", "sheep", "deer", "-pox", '[A-Z].*ese', "-itis"); // will return original word + private static $user_defined_inflections = array( + // "word"=>"inflection", + "role"=>"roles", +/* "comment"=>"comments", + "user"=>"users", + "word"=>"words", + "category"=>"categories", + "file"=>"files", + "post"=>"posts", + "tag"=>"tags", + "role"=>"roles", + "activity"=>"activities", + "event"=>"events", + "favorite"=>"favorites", + "photo"=>"photos", + "link"=>"links", + "privilege"=>"privileges", +*/ ); // defined by the user by define_inflection() + private static $irregular_words = array( + 'beef'=>'beefs', + 'brother'=>'brothers', + 'child'=>'children', + 'person'=>'people', + 'cow'=>'cows', + 'ephemeris'=>'ephemerides', + 'genie'=>'genies', + 'money'=>'monies', + 'mongoose'=>'mongooses', + 'mythos'=>'mythoi', + 'octopus'=>'octopuses', + 'ox'=>'oxen', + 'soliloquy'=>'soliloquies', + 'trilby'=>'trilbys', + ); + private static $irregular_inflections = array( + '-man'=>'-men', + '-[lm]ouse'=>'-ice', + '-tooth'=>'-teeth', + '-goose'=>'-geese', + '-foot'=>'-feet', + '-zoon'=>'-zoa', + // '-[csx]is'=>'-es', + ); + private static $classical_inflections = array( + '-ex'=>'-ices', + '-um'=>'-a', + '-on'=>'-a', + '-a'=>'-ae', + ); + private static $es = array( + '-ch'=>'-ches', + '-sh'=>'-shes', + '-ss'=>'-sses', + ); + private static $f = array( + '-f'=>'-ves', + ); + private static $y = array( + '-[aeiou]y'=>'-ys', + '-[A-Z].*y'=>'-ys', + '-y'=>'-ies', + ); + private static $o = array( + '-[aeiou]o'=>'-os', + '-o'=>'-oes', + ); + + // pluralize the word + public function pluralize($word) { + // run the gamut + if($inflection = self::run_gamut($word)) return $inflection; + + // if it ends in -s, pluralize it with -es + if(substr($word, -1, 1) == 's') return "{$word}es"; + + // otherwise, just add an -s to the word + return "{$word}s"; + } + public function singularize($word) { + // run the gamut + if($inflection = self::run_gamut($word)) return $inflection; + + // if it ends in -es, remove it and return + if(substr($word, -2, 2) == 'es') return substr($word, 0, -2); + + // otherwise, if the word ends in -s, remove it and return it + if(substr($word, -1, 1) == 's') return substr($word, 0, -1); + return $word; + } + private static function run_gamut($word) { + if($inflection = self::user_defined($word)) return $inflection; + + // return the word if it's the same plural or singular + if(self::does_not_inflect($word)) return $word; + + // normally we'd handle pronouns here, but I don't see any point in doing that for this, + // but it could always be fleshed out in the future to include this functionality. + + // check for irregular words and inflections + if($inflection = self::irregular($word)) return $inflection; + + // check for classical inflections + if($inflection = self::classical($word)) return $inflection; + + // check for -es inflections + if($inflection = self::es($word)) return $inflection; + + // check for -f inflections + if($inflection = self::f($word)) return $inflection; + + // check for -y inflections + if($inflection = self::y($word)) return $inflection; + + // check for -o inflections + if($inflection = self::o($word)) return $inflection; + + // none of these, so return false to signify no change + return false; + } + + // set user defined inflections + public static function define_inflection($inflection) { + // @desc alias for define_inflections($inflections) + define_inflections($inflection); + } + public static function define_inflections($inflections) { + // @desc defines numerous inflections + // @format ["word"=>"inflection", ...] + foreach($inflections as $word=>$inflection) self::$user_defined_inflections[$word] = $inflection; + } + private static function user_defined($word) { + // @desc returns the inflected word if it's been defined by the user... false if not + if(array_key_exists($word, self::$user_defined_inflections)) { + return self::$user_defined_inflections[$word]; + } + if(in_array($word, self::$user_defined_inflections)) { + return array_search($word, self::$user_defined_inflections); + } + + return false; + } + private static function does_not_inflect($word) { + // check to see if a word does not inflect + foreach(self::$words_that_do_not_inflect_in_the_plural as $noninflector) { + if(substr($noninflector, 0, 1) == '-') $noninflector = '.*' . substr($noninflector, 1); + if(preg_match("/{$noninflector}/", $word) == 1) { + // print "Warning: noninflector detected ({$word})\n"; + return true; // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function irregular($word) { + // @desc returns irregular forms of words + + // check if it's an irregular word + if(array_key_exists($word, self::$irregular_words)) { + return self::$irregular_words[$word]; + } + if(in_array($word, self::$irregular_words)) { + return array_search($word, self::$irregular_words); + } + + // if it hasn't matched yet, then check to see if it's an irregular inflection + foreach(self::$irregular_inflections as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + $inflection_root = preg_replace('/(.*)(\[.*\])(.*)/', '$1$3', $inflection_root); + $inflected_form = preg_replace('/(.*)(\[.*\])(.*)/', '$1$3', $inflected_form); + // print "Warning: irregular inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$irregular_inflections as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + $inflection_root = preg_replace('/(.*)(\[.*\])(.*)/', '$1$3', $inflection_root); + $inflected_form = preg_replace('/(.*)(\[.*\])(.*)/', '$1$3', $inflected_form); + // print "Warning: irregular inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function classical($word) { + // check to see if it's a classical inflection + foreach(self::$classical_inflections as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: classical inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$classical_inflections as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: classical inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function es($word) { + // @desc returns the inflection of an -es inflected/inflectable word + // check to see if it's an -es inflection + foreach(self::$es as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -es inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$es as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -es inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function f($word) { + // @desc returns the inflection of an -f inflected/inflectable word + // check to see if it's an -f inflection + foreach(self::$f as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -f inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$f as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -f inflection detected ({$word})\n"; + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function y($word) { + // @desc returns the inflection of a -y inflected/inflectable word + // check to see if it's a -y inflection + foreach(self::$y as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -y inflection detected ({$word})\n"; + return self::inflect($word, $inflection_root, $inflected_form); + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$y as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -y inflection detected ({$word})\n"; + return self::inflect($word, $inflection_root, $inflected_form); + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + + return false; + } + private static function o($word) { + // @desc returns the inflection of an -o inflected/inflectable word + // check to see if it's an -o inflection + foreach(self::$o as $inflection=>$inflected_form) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -o inflection detected ({$word})\n"; + return self::inflect($word, $inflection_root, $inflected_form); + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + // now for singular form + foreach(self::$o as $inflected_form=>$inflection) { + $inflection_root = substr($inflection, 1); + $inflection = ".*{$inflection_root}"; + $inflected_form = substr($inflected_form, 1); + if(preg_match("/{$inflection}$/", $word) == 1) { + // print "Warning: -o inflection detected ({$word})\n"; + return self::inflect($word, $inflection_root, $inflected_form); + return str_replace($inflection_root, $inflected_form, $word); // if the word matches the regex (once), then return the word + } + } + return false; + } + + // action functions + private static function inflect($word, $ending, $inflection) { + $ending = str_replace('.*', '', str_replace('[A-Z]', '', str_replace('[aeiou]', '', $ending))); + $inflection = str_replace('.*', '', str_replace('[A-Z]', '', str_replace('[aeiou]', '', $inflection))); + // $ending = preg_replace('/(.*)(\[\w*\]|\W*)(.*)$/', '$1$3', $ending); + // $inflection = preg_replace('/(.*)(\[\w*\]|\W*)(.*)$/', '$1$3', $inflection); + return preg_replace("/(\w+){$ending}$/", '$1' . $inflection, $word); + } + + // get pluralization + public static function __get($word) { + return self::pluralize($word); + } + public static function __set($word, $number) { + if($number > 1) return self::pluralize($word); + return $word; + } + } + + class PluralizeException extends StdException {} + + // stolen from Rails' active_support + /*# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, + # and class names to foreign keys. + module Inflector + extend self + + def pluralize(word) + result = word.to_s.dup + + if uncountable_words.include?(result.downcase) + result + else + plural_rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + result + end + end + + def singularize(word) + result = word.to_s.dup + + if uncountable_words.include?(result.downcase) + result + else + singular_rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + result + end + end + + def camelize(lower_case_and_underscored_word) + lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase } + end + + def underscore(camel_cased_word) + camel_cased_word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase + end + + def humanize(lower_case_and_underscored_word) + lower_case_and_underscored_word.to_s.gsub(/_/, " ").capitalize + end + + def demodulize(class_name_in_module) + class_name_in_module.to_s.gsub(/^.*::/, '') + end + + def tableize(class_name) + pluralize(underscore(class_name)) + end + + def classify(table_name) + camelize(singularize(table_name)) + end + + def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) + Inflector.underscore(Inflector.demodulize(class_name)) + + (separate_class_name_and_id_with_underscore ? "_id" : "id") + end + + def constantize(camel_cased_word) + camel_cased_word.split("::").inject(Object) do |final_type, part| + final_type = final_type.const_get(part) + end + end + + private + def uncountable_words #:doc + %w( equipment information rice money species series fish ) + end + + def plural_rules #:doc: + [ + [/^(ox)$/i, '\1\2en'], # ox + [/([m|l])ouse$/i, '\1ice'], # mouse, louse + [/(matr|vert)ix|ex$/i, '\1ices'], # matrix, vertex, index + [/(x|ch|ss|sh)$/i, '\1es'], # search, switch, fix, box, process, address + [/([^aeiouy]|qu)ies$/i, '\1y'], + [/([^aeiouy]|qu)y$/i, '\1ies'], # query, ability, agency + [/(hive)$/i, '\1s'], # archive, hive + [/(?:([^f])fe|([lr])f)$/i, '\1\2ves'], # half, safe, wife + [/sis$/i, 'ses'], # basis, diagnosis + [/([ti])um$/i, '\1a'], # datum, medium + [/(p)erson$/i, '\1eople'], # person, salesperson + [/(m)an$/i, '\1en'], # man, woman, spokesman + [/(c)hild$/i, '\1hildren'], # child + [/(buffal|tomat)o$/i, '\1\2oes'], # buffalo, tomato + [/(bu)s$/i, '\1\2ses'], # bus + [/(alias)/i, '\1es'], # alias + [/(octop|vir)us$/i, '\1i'], # octopus, virus - virus has no defined plural (according to Latin/dictionary.com), but viri is better than viruses/viruss + [/(ax|cri|test)is$/i, '\1es'], # axis, crisis + [/s$/i, 's'], # no change (compatibility) + [/$/, 's'] + ] + end + + def singular_rules #:doc: + [ + [/(matr)ices$/i, '\1ix'], + [/(vert)ices$/i, '\1ex'], + [/^(ox)en/i, '\1'], + [/(alias)es$/i, '\1'], + [/([octop|vir])i$/i, '\1us'], + [/(cris|ax|test)es$/i, '\1is'], + [/(shoe)s$/i, '\1'], + [/(o)es$/i, '\1'], + [/(bus)es$/i, '\1'], + [/([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'], + [/([^f])ves$/i, '\1fe'], + [/(^analy)ses$/i, '\1sis'], + [/((a)naly|(b)a|(d)iagno|(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'], + [/(s)tatus$/i, '\1\2tatus'], + [/(c)hildren$/i, '\1\2hild'], + [/(n)ews$/i, '\1\2ews'], + [/s$/i, ''] + ] + end + end*/ +?> \ No newline at end of file diff --git a/library/interfaces/Adapter.php b/library/interfaces/Adapter.php new file mode 100755 index 0000000..f87247b --- /dev/null +++ b/library/interfaces/Adapter.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/library/interfaces/Model.php b/library/interfaces/Model.php new file mode 100755 index 0000000..7fa0105 --- /dev/null +++ b/library/interfaces/Model.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/library/interfaces/_adapter.php b/library/interfaces/_adapter.php new file mode 100755 index 0000000..496b133 --- /dev/null +++ b/library/interfaces/_adapter.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/library/smarty/Config_File.class.php b/library/smarty/Config_File.class.php new file mode 100755 index 0000000..f75efd7 --- /dev/null +++ b/library/smarty/Config_File.class.php @@ -0,0 +1,389 @@ + + * @access public + * @package Smarty + */ + +/* $Id: Config_File.class.php,v 1.1.1.1 2006/04/26 20:43:08 bsimpson Exp $ */ + +/** + * Config file reading class + * @package Smarty + */ +class Config_File { + /**#@+ + * Options + * @var boolean + */ + /** + * Controls whether variables with the same name overwrite each other. + */ + var $overwrite = true; + + /** + * Controls whether config values of on/true/yes and off/false/no get + * converted to boolean values automatically. + */ + var $booleanize = true; + + /** + * Controls whether hidden config sections/vars are read from the file. + */ + var $read_hidden = true; + + /** + * Controls whether or not to fix mac or dos formatted newlines. + * If set to true, \r or \r\n will be changed to \n. + */ + var $fix_newlines = true; + /**#@-*/ + + /** @access private */ + var $_config_path = ""; + var $_config_data = array(); + /**#@-*/ + + /** + * Constructs a new config file class. + * + * @param string $config_path (optional) path to the config files + */ + function Config_File($config_path = NULL) + { + if (isset($config_path)) + $this->set_path($config_path); + } + + + /** + * Set the path where configuration files can be found. + * + * @param string $config_path path to the config files + */ + function set_path($config_path) + { + if (!empty($config_path)) { + if (!is_string($config_path) || !file_exists($config_path) || !is_dir($config_path)) { + $this->_trigger_error_msg("Bad config file path '$config_path'"); + return; + } + if(substr($config_path, -1) != DIRECTORY_SEPARATOR) { + $config_path .= DIRECTORY_SEPARATOR; + } + + $this->_config_path = $config_path; + } + } + + + /** + * Retrieves config info based on the file, section, and variable name. + * + * @param string $file_name config file to get info for + * @param string $section_name (optional) section to get info for + * @param string $var_name (optional) variable to get info for + * @return string|array a value or array of values + */ + function get($file_name, $section_name = NULL, $var_name = NULL) + { + if (empty($file_name)) { + $this->_trigger_error_msg('Empty config file name'); + return; + } else { + $file_name = $this->_config_path . $file_name; + if (!isset($this->_config_data[$file_name])) + $this->load_file($file_name, false); + } + + if (!empty($var_name)) { + if (empty($section_name)) { + return $this->_config_data[$file_name]["vars"][$var_name]; + } else { + if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name])) + return $this->_config_data[$file_name]["sections"][$section_name]["vars"][$var_name]; + else + return array(); + } + } else { + if (empty($section_name)) { + return (array)$this->_config_data[$file_name]["vars"]; + } else { + if(isset($this->_config_data[$file_name]["sections"][$section_name]["vars"])) + return (array)$this->_config_data[$file_name]["sections"][$section_name]["vars"]; + else + return array(); + } + } + } + + + /** + * Retrieves config info based on the key. + * + * @param $file_name string config key (filename/section/var) + * @return string|array same as get() + * @uses get() retrieves information from config file and returns it + */ + function &get_key($config_key) + { + list($file_name, $section_name, $var_name) = explode('/', $config_key, 3); + $result = &$this->get($file_name, $section_name, $var_name); + return $result; + } + + /** + * Get all loaded config file names. + * + * @return array an array of loaded config file names + */ + function get_file_names() + { + return array_keys($this->_config_data); + } + + + /** + * Get all section names from a loaded file. + * + * @param string $file_name config file to get section names from + * @return array an array of section names from the specified file + */ + function get_section_names($file_name) + { + $file_name = $this->_config_path . $file_name; + if (!isset($this->_config_data[$file_name])) { + $this->_trigger_error_msg("Unknown config file '$file_name'"); + return; + } + + return array_keys($this->_config_data[$file_name]["sections"]); + } + + + /** + * Get all global or section variable names. + * + * @param string $file_name config file to get info for + * @param string $section_name (optional) section to get info for + * @return array an array of variables names from the specified file/section + */ + function get_var_names($file_name, $section = NULL) + { + if (empty($file_name)) { + $this->_trigger_error_msg('Empty config file name'); + return; + } else if (!isset($this->_config_data[$file_name])) { + $this->_trigger_error_msg("Unknown config file '$file_name'"); + return; + } + + if (empty($section)) + return array_keys($this->_config_data[$file_name]["vars"]); + else + return array_keys($this->_config_data[$file_name]["sections"][$section]["vars"]); + } + + + /** + * Clear loaded config data for a certain file or all files. + * + * @param string $file_name file to clear config data for + */ + function clear($file_name = NULL) + { + if ($file_name === NULL) + $this->_config_data = array(); + else if (isset($this->_config_data[$file_name])) + $this->_config_data[$file_name] = array(); + } + + + /** + * Load a configuration file manually. + * + * @param string $file_name file name to load + * @param boolean $prepend_path whether current config path should be + * prepended to the filename + */ + function load_file($file_name, $prepend_path = true) + { + if ($prepend_path && $this->_config_path != "") + $config_file = $this->_config_path . $file_name; + else + $config_file = $file_name; + + ini_set('track_errors', true); + $fp = @fopen($config_file, "r"); + if (!is_resource($fp)) { + $this->_trigger_error_msg("Could not open config file '$config_file'"); + return false; + } + + $contents = ($size = filesize($config_file)) ? fread($fp, $size) : ''; + fclose($fp); + + $this->_config_data[$config_file] = $this->parse_contents($contents); + return true; + } + + /** + * Store the contents of a file manually. + * + * @param string $config_file file name of the related contents + * @param string $contents the file-contents to parse + */ + function set_file_contents($config_file, $contents) + { + $this->_config_data[$config_file] = $this->parse_contents($contents); + return true; + } + + /** + * parse the source of a configuration file manually. + * + * @param string $contents the file-contents to parse + */ + function parse_contents($contents) + { + if($this->fix_newlines) { + // fix mac/dos formatted newlines + $contents = preg_replace('!\r\n?!', "\n", $contents); + } + + $config_data = array(); + $config_data['sections'] = array(); + $config_data['vars'] = array(); + + /* reference to fill with data */ + $vars =& $config_data['vars']; + + /* parse file line by line */ + preg_match_all('!^.*\r?\n?!m', $contents, $match); + $lines = $match[0]; + for ($i=0, $count=count($lines); $i<$count; $i++) { + $line = $lines[$i]; + if (empty($line)) continue; + + if ( substr($line, 0, 1) == '[' && preg_match('!^\[(.*?)\]!', $line, $match) ) { + /* section found */ + if (substr($match[1], 0, 1) == '.') { + /* hidden section */ + if ($this->read_hidden) { + $section_name = substr($match[1], 1); + } else { + /* break reference to $vars to ignore hidden section */ + unset($vars); + $vars = array(); + continue; + } + } else { + $section_name = $match[1]; + } + if (!isset($config_data['sections'][$section_name])) + $config_data['sections'][$section_name] = array('vars' => array()); + $vars =& $config_data['sections'][$section_name]['vars']; + continue; + } + + if (preg_match('/^\s*(\.?\w+)\s*=\s*(.*)/s', $line, $match)) { + /* variable found */ + $var_name = rtrim($match[1]); + if (strpos($match[2], '"""') === 0) { + /* handle multiline-value */ + $lines[$i] = substr($match[2], 3); + $var_value = ''; + while ($i<$count) { + if (($pos = strpos($lines[$i], '"""')) === false) { + $var_value .= $lines[$i++]; + } else { + /* end of multiline-value */ + $var_value .= substr($lines[$i], 0, $pos); + break; + } + } + $booleanize = false; + + } else { + /* handle simple value */ + $var_value = preg_replace('/^([\'"])(.*)\1$/', '\2', rtrim($match[2])); + $booleanize = $this->booleanize; + + } + $this->_set_config_var($vars, $var_name, $var_value, $booleanize); + } + /* else unparsable line / means it is a comment / means ignore it */ + } + return $config_data; + } + + /**#@+ @access private */ + /** + * @param array &$container + * @param string $var_name + * @param mixed $var_value + * @param boolean $booleanize determines whether $var_value is converted to + * to true/false + */ + function _set_config_var(&$container, $var_name, $var_value, $booleanize) + { + if (substr($var_name, 0, 1) == '.') { + if (!$this->read_hidden) + return; + else + $var_name = substr($var_name, 1); + } + + if (!preg_match("/^[a-zA-Z_]\w*$/", $var_name)) { + $this->_trigger_error_msg("Bad variable name '$var_name'"); + return; + } + + if ($booleanize) { + if (preg_match("/^(on|true|yes)$/i", $var_value)) + $var_value = true; + else if (preg_match("/^(off|false|no)$/i", $var_value)) + $var_value = false; + } + + if (!isset($container[$var_name]) || $this->overwrite) + $container[$var_name] = $var_value; + else { + settype($container[$var_name], 'array'); + $container[$var_name][] = $var_value; + } + } + + /** + * @uses trigger_error() creates a PHP warning/error + * @param string $error_msg + * @param integer $error_type one of + */ + function _trigger_error_msg($error_msg, $error_type = E_USER_WARNING) + { + trigger_error("Config_File error: $error_msg", $error_type); + } + /**#@-*/ +} + +?> diff --git a/library/smarty/Smarty.class.php b/library/smarty/Smarty.class.php new file mode 100755 index 0000000..f7a6422 --- /dev/null +++ b/library/smarty/Smarty.class.php @@ -0,0 +1,1941 @@ + + * @author Andrei Zmievski + * @package Smarty + * @version 2.6.11 + */ + +/* $Id: Smarty.class.php,v 1.1.1.1 2006/04/26 20:43:08 bsimpson Exp $ */ + +/** + * DIR_SEP isn't used anymore, but third party apps might + */ +if(!defined('DIR_SEP')) { + define('DIR_SEP', DIRECTORY_SEPARATOR); +} + +/** + * set SMARTY_DIR to absolute path to Smarty library files. + * if not defined, include_path will be used. Sets SMARTY_DIR only if user + * application has not already defined it. + */ + +if (!defined('SMARTY_DIR')) { + define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); +} + +if (!defined('SMARTY_CORE_DIR')) { + define('SMARTY_CORE_DIR', SMARTY_DIR . 'internals' . DIRECTORY_SEPARATOR); +} + +define('SMARTY_PHP_PASSTHRU', 0); +define('SMARTY_PHP_QUOTE', 1); +define('SMARTY_PHP_REMOVE', 2); +define('SMARTY_PHP_ALLOW', 3); + +/** + * @package Smarty + */ +class Smarty +{ + /**#@+ + * Smarty Configuration Section + */ + + /** + * The name of the directory where templates are located. + * + * @var string + */ + var $template_dir = 'templates'; + + /** + * The directory where compiled templates are located. + * + * @var string + */ + var $compile_dir = 'templates_c'; + + /** + * The directory where config files are located. + * + * @var string + */ + var $config_dir = 'configs'; + + /** + * An array of directories searched for plugins. + * + * @var array + */ + var $plugins_dir = array('plugins'); + + /** + * If debugging is enabled, a debug console window will display + * when the page loads (make sure your browser allows unrequested + * popup windows) + * + * @var boolean + */ + var $debugging = false; + + /** + * When set, smarty does uses this value as error_reporting-level. + * + * @var boolean + */ + var $error_reporting = null; + + /** + * This is the path to the debug console template. If not set, + * the default one will be used. + * + * @var string + */ + var $debug_tpl = ''; + + /** + * This determines if debugging is enable-able from the browser. + *
      + *
    • NONE => no debugging control allowed
    • + *
    • URL => enable debugging when SMARTY_DEBUG is found in the URL.
    • + *
    + * @link http://www.foo.dom/index.php?SMARTY_DEBUG + * @var string + */ + var $debugging_ctrl = 'NONE'; + + /** + * This tells Smarty whether to check for recompiling or not. Recompiling + * does not need to happen unless a template or config file is changed. + * Typically you enable this during development, and disable for + * production. + * + * @var boolean + */ + var $compile_check = true; + + /** + * This forces templates to compile every time. Useful for development + * or debugging. + * + * @var boolean + */ + var $force_compile = false; + + /** + * This enables template caching. + *
      + *
    • 0 = no caching
    • + *
    • 1 = use class cache_lifetime value
    • + *
    • 2 = use cache_lifetime in cache file
    • + *
    + * @var integer + */ + var $caching = 0; + + /** + * The name of the directory for cache files. + * + * @var string + */ + var $cache_dir = 'cache'; + + /** + * This is the number of seconds cached content will persist. + *
      + *
    • 0 = always regenerate cache
    • + *
    • -1 = never expires
    • + *
    + * + * @var integer + */ + var $cache_lifetime = 3600; + + /** + * Only used when $caching is enabled. If true, then If-Modified-Since headers + * are respected with cached content, and appropriate HTTP headers are sent. + * This way repeated hits to a cached page do not send the entire page to the + * client every time. + * + * @var boolean + */ + var $cache_modified_check = false; + + /** + * This determines how Smarty handles "" tags in templates. + * possible values: + *
      + *
    • SMARTY_PHP_PASSTHRU -> print tags as plain text
    • + *
    • SMARTY_PHP_QUOTE -> escape tags as entities
    • + *
    • SMARTY_PHP_REMOVE -> remove php tags
    • + *
    • SMARTY_PHP_ALLOW -> execute php tags
    • + *
    + * + * @var integer + */ + var $php_handling = SMARTY_PHP_PASSTHRU; + + /** + * This enables template security. When enabled, many things are restricted + * in the templates that normally would go unchecked. This is useful when + * untrusted parties are editing templates and you want a reasonable level + * of security. (no direct execution of PHP in templates for example) + * + * @var boolean + */ + var $security = false; + + /** + * This is the list of template directories that are considered secure. This + * is used only if {@link $security} is enabled. One directory per array + * element. {@link $template_dir} is in this list implicitly. + * + * @var array + */ + var $secure_dir = array(); + + /** + * These are the security settings for Smarty. They are used only when + * {@link $security} is enabled. + * + * @var array + */ + var $security_settings = array( + 'PHP_HANDLING' => false, + 'IF_FUNCS' => array('array', 'list', + 'isset', 'empty', + 'count', 'sizeof', + 'in_array', 'is_array', + 'true', 'false', 'null'), + 'INCLUDE_ANY' => false, + 'PHP_TAGS' => false, + 'MODIFIER_FUNCS' => array('count'), + 'ALLOW_CONSTANTS' => false + ); + + /** + * This is an array of directories where trusted php scripts reside. + * {@link $security} is disabled during their inclusion/execution. + * + * @var array + */ + var $trusted_dir = array(); + + /** + * The left delimiter used for the template tags. + * + * @var string + */ + var $left_delimiter = '{'; + + /** + * The right delimiter used for the template tags. + * + * @var string + */ + var $right_delimiter = '}'; + + /** + * The order in which request variables are registered, similar to + * variables_order in php.ini E = Environment, G = GET, P = POST, + * C = Cookies, S = Server + * + * @var string + */ + var $request_vars_order = 'EGPCS'; + + /** + * Indicates wether $HTTP_*_VARS[] (request_use_auto_globals=false) + * are uses as request-vars or $_*[]-vars. note: if + * request_use_auto_globals is true, then $request_vars_order has + * no effect, but the php-ini-value "gpc_order" + * + * @var boolean + */ + var $request_use_auto_globals = true; + + /** + * Set this if you want different sets of compiled files for the same + * templates. This is useful for things like different languages. + * Instead of creating separate sets of templates per language, you + * set different compile_ids like 'en' and 'de'. + * + * @var string + */ + var $compile_id = null; + + /** + * This tells Smarty whether or not to use sub dirs in the cache/ and + * templates_c/ directories. sub directories better organized, but + * may not work well with PHP safe mode enabled. + * + * @var boolean + * + */ + var $use_sub_dirs = false; + + /** + * This is a list of the modifiers to apply to all template variables. + * Put each modifier in a separate array element in the order you want + * them applied. example: array('escape:"htmlall"'); + * + * @var array + */ + var $default_modifiers = array(); + + /** + * This is the resource type to be used when not specified + * at the beginning of the resource path. examples: + * $smarty->display('file:index.tpl'); + * $smarty->display('db:index.tpl'); + * $smarty->display('index.tpl'); // will use default resource type + * {include file="file:index.tpl"} + * {include file="db:index.tpl"} + * {include file="index.tpl"} {* will use default resource type *} + * + * @var array + */ + var $default_resource_type = 'file'; + + /** + * The function used for cache file handling. If not set, built-in caching is used. + * + * @var null|string function name + */ + var $cache_handler_func = null; + + /** + * This indicates which filters are automatically loaded into Smarty. + * + * @var array array of filter names + */ + var $autoload_filters = array(); + + /**#@+ + * @var boolean + */ + /** + * This tells if config file vars of the same name overwrite each other or not. + * if disabled, same name variables are accumulated in an array. + */ + var $config_overwrite = true; + + /** + * This tells whether or not to automatically booleanize config file variables. + * If enabled, then the strings "on", "true", and "yes" are treated as boolean + * true, and "off", "false" and "no" are treated as boolean false. + */ + var $config_booleanize = true; + + /** + * This tells whether hidden sections [.foobar] are readable from the + * tempalates or not. Normally you would never allow this since that is + * the point behind hidden sections: the application can access them, but + * the templates cannot. + */ + var $config_read_hidden = false; + + /** + * This tells whether or not automatically fix newlines in config files. + * It basically converts \r (mac) or \r\n (dos) to \n + */ + var $config_fix_newlines = true; + /**#@-*/ + + /** + * If a template cannot be found, this PHP function will be executed. + * Useful for creating templates on-the-fly or other special action. + * + * @var string function name + */ + var $default_template_handler_func = ''; + + /** + * The file that contains the compiler class. This can a full + * pathname, or relative to the php_include path. + * + * @var string + */ + var $compiler_file = 'Smarty_Compiler.class.php'; + + /** + * The class used for compiling templates. + * + * @var string + */ + var $compiler_class = 'Smarty_Compiler'; + + /** + * The class used to load config vars. + * + * @var string + */ + var $config_class = 'Config_File'; + +/**#@+ + * END Smarty Configuration Section + * There should be no need to touch anything below this line. + * @access private + */ + /** + * where assigned template vars are kept + * + * @var array + */ + var $_tpl_vars = array(); + + /** + * stores run-time $smarty.* vars + * + * @var null|array + */ + var $_smarty_vars = null; + + /** + * keeps track of sections + * + * @var array + */ + var $_sections = array(); + + /** + * keeps track of foreach blocks + * + * @var array + */ + var $_foreach = array(); + + /** + * keeps track of tag hierarchy + * + * @var array + */ + var $_tag_stack = array(); + + /** + * configuration object + * + * @var Config_file + */ + var $_conf_obj = null; + + /** + * loaded configuration settings + * + * @var array + */ + var $_config = array(array('vars' => array(), 'files' => array())); + + /** + * md5 checksum of the string 'Smarty' + * + * @var string + */ + var $_smarty_md5 = 'f8d698aea36fcbead2b9d5359ffca76f'; + + /** + * Smarty version number + * + * @var string + */ + var $_version = '2.6.11'; + + /** + * current template inclusion depth + * + * @var integer + */ + var $_inclusion_depth = 0; + + /** + * for different compiled templates + * + * @var string + */ + var $_compile_id = null; + + /** + * text in URL to enable debug mode + * + * @var string + */ + var $_smarty_debug_id = 'SMARTY_DEBUG'; + + /** + * debugging information for debug console + * + * @var array + */ + var $_smarty_debug_info = array(); + + /** + * info that makes up a cache file + * + * @var array + */ + var $_cache_info = array(); + + /** + * default file permissions + * + * @var integer + */ + var $_file_perms = 0644; + + /** + * default dir permissions + * + * @var integer + */ + var $_dir_perms = 0771; + + /** + * registered objects + * + * @var array + */ + var $_reg_objects = array(); + + /** + * table keeping track of plugins + * + * @var array + */ + var $_plugins = array( + 'modifier' => array(), + 'function' => array(), + 'block' => array(), + 'compiler' => array(), + 'prefilter' => array(), + 'postfilter' => array(), + 'outputfilter' => array(), + 'resource' => array(), + 'insert' => array()); + + + /** + * cache serials + * + * @var array + */ + var $_cache_serials = array(); + + /** + * name of optional cache include file + * + * @var string + */ + var $_cache_include = null; + + /** + * indicate if the current code is used in a compiled + * include + * + * @var string + */ + var $_cache_including = false; + + /**#@-*/ + /** + * The class constructor. + */ + function Smarty() + { + $this->assign('SCRIPT_NAME', isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] + : @$GLOBALS['HTTP_SERVER_VARS']['SCRIPT_NAME']); + } + + /** + * assigns values to template variables + * + * @param array|string $tpl_var the template variable name(s) + * @param mixed $value the value to assign + */ + function assign($tpl_var, $value = null) + { + if (is_array($tpl_var)){ + foreach ($tpl_var as $key => $val) { + if ($key != '') { + $this->_tpl_vars[$key] = $val; + } + } + } else { + if ($tpl_var != '') + $this->_tpl_vars[$tpl_var] = $value; + } + } + + /** + * assigns values to template variables by reference + * + * @param string $tpl_var the template variable name + * @param mixed $value the referenced value to assign + */ + function assign_by_ref($tpl_var, &$value) + { + if ($tpl_var != '') + $this->_tpl_vars[$tpl_var] = &$value; + } + + /** + * appends values to template variables + * + * @param array|string $tpl_var the template variable name(s) + * @param mixed $value the value to append + */ + function append($tpl_var, $value=null, $merge=false) + { + if (is_array($tpl_var)) { + // $tpl_var is an array, ignore $value + foreach ($tpl_var as $_key => $_val) { + if ($_key != '') { + if(!@is_array($this->_tpl_vars[$_key])) { + settype($this->_tpl_vars[$_key],'array'); + } + if($merge && is_array($_val)) { + foreach($_val as $_mkey => $_mval) { + $this->_tpl_vars[$_key][$_mkey] = $_mval; + } + } else { + $this->_tpl_vars[$_key][] = $_val; + } + } + } + } else { + if ($tpl_var != '' && isset($value)) { + if(!@is_array($this->_tpl_vars[$tpl_var])) { + settype($this->_tpl_vars[$tpl_var],'array'); + } + if($merge && is_array($value)) { + foreach($value as $_mkey => $_mval) { + $this->_tpl_vars[$tpl_var][$_mkey] = $_mval; + } + } else { + $this->_tpl_vars[$tpl_var][] = $value; + } + } + } + } + + /** + * appends values to template variables by reference + * + * @param string $tpl_var the template variable name + * @param mixed $value the referenced value to append + */ + function append_by_ref($tpl_var, &$value, $merge=false) + { + if ($tpl_var != '' && isset($value)) { + if(!@is_array($this->_tpl_vars[$tpl_var])) { + settype($this->_tpl_vars[$tpl_var],'array'); + } + if ($merge && is_array($value)) { + foreach($value as $_key => $_val) { + $this->_tpl_vars[$tpl_var][$_key] = &$value[$_key]; + } + } else { + $this->_tpl_vars[$tpl_var][] = &$value; + } + } + } + + + /** + * clear the given assigned template variable. + * + * @param string $tpl_var the template variable to clear + */ + function clear_assign($tpl_var) + { + if (is_array($tpl_var)) + foreach ($tpl_var as $curr_var) + unset($this->_tpl_vars[$curr_var]); + else + unset($this->_tpl_vars[$tpl_var]); + } + + + /** + * Registers custom function to be used in templates + * + * @param string $function the name of the template function + * @param string $function_impl the name of the PHP function to register + */ + function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null) + { + $this->_plugins['function'][$function] = + array($function_impl, null, null, false, $cacheable, $cache_attrs); + + } + + /** + * Unregisters custom function + * + * @param string $function name of template function + */ + function unregister_function($function) + { + unset($this->_plugins['function'][$function]); + } + + /** + * Registers object to be used in templates + * + * @param string $object name of template object + * @param object &$object_impl the referenced PHP object to register + * @param null|array $allowed list of allowed methods (empty = all) + * @param boolean $smarty_args smarty argument format, else traditional + * @param null|array $block_functs list of methods that are block format + */ + function register_object($object, &$object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) + { + settype($allowed, 'array'); + settype($smarty_args, 'boolean'); + $this->_reg_objects[$object] = + array(&$object_impl, $allowed, $smarty_args, $block_methods); + } + + /** + * Unregisters object + * + * @param string $object name of template object + */ + function unregister_object($object) + { + unset($this->_reg_objects[$object]); + } + + + /** + * Registers block function to be used in templates + * + * @param string $block name of template block + * @param string $block_impl PHP function to register + */ + function register_block($block, $block_impl, $cacheable=true, $cache_attrs=null) + { + $this->_plugins['block'][$block] = + array($block_impl, null, null, false, $cacheable, $cache_attrs); + } + + /** + * Unregisters block function + * + * @param string $block name of template function + */ + function unregister_block($block) + { + unset($this->_plugins['block'][$block]); + } + + /** + * Registers compiler function + * + * @param string $function name of template function + * @param string $function_impl name of PHP function to register + */ + function register_compiler_function($function, $function_impl, $cacheable=true) + { + $this->_plugins['compiler'][$function] = + array($function_impl, null, null, false, $cacheable); + } + + /** + * Unregisters compiler function + * + * @param string $function name of template function + */ + function unregister_compiler_function($function) + { + unset($this->_plugins['compiler'][$function]); + } + + /** + * Registers modifier to be used in templates + * + * @param string $modifier name of template modifier + * @param string $modifier_impl name of PHP function to register + */ + function register_modifier($modifier, $modifier_impl) + { + $this->_plugins['modifier'][$modifier] = + array($modifier_impl, null, null, false); + } + + /** + * Unregisters modifier + * + * @param string $modifier name of template modifier + */ + function unregister_modifier($modifier) + { + unset($this->_plugins['modifier'][$modifier]); + } + + /** + * Registers a resource to fetch a template + * + * @param string $type name of resource + * @param array $functions array of functions to handle resource + */ + function register_resource($type, $functions) + { + if (count($functions)==4) { + $this->_plugins['resource'][$type] = + array($functions, false); + + } elseif (count($functions)==5) { + $this->_plugins['resource'][$type] = + array(array(array(&$functions[0], $functions[1]) + ,array(&$functions[0], $functions[2]) + ,array(&$functions[0], $functions[3]) + ,array(&$functions[0], $functions[4])) + ,false); + + } else { + $this->trigger_error("malformed function-list for '$type' in register_resource"); + + } + } + + /** + * Unregisters a resource + * + * @param string $type name of resource + */ + function unregister_resource($type) + { + unset($this->_plugins['resource'][$type]); + } + + /** + * Registers a prefilter function to apply + * to a template before compiling + * + * @param string $function name of PHP function to register + */ + function register_prefilter($function) + { + $_name = (is_array($function)) ? $function[1] : $function; + $this->_plugins['prefilter'][$_name] + = array($function, null, null, false); + } + + /** + * Unregisters a prefilter function + * + * @param string $function name of PHP function + */ + function unregister_prefilter($function) + { + unset($this->_plugins['prefilter'][$function]); + } + + /** + * Registers a postfilter function to apply + * to a compiled template after compilation + * + * @param string $function name of PHP function to register + */ + function register_postfilter($function) + { + $_name = (is_array($function)) ? $function[1] : $function; + $this->_plugins['postfilter'][$_name] + = array($function, null, null, false); + } + + /** + * Unregisters a postfilter function + * + * @param string $function name of PHP function + */ + function unregister_postfilter($function) + { + unset($this->_plugins['postfilter'][$function]); + } + + /** + * Registers an output filter function to apply + * to a template output + * + * @param string $function name of PHP function + */ + function register_outputfilter($function) + { + $_name = (is_array($function)) ? $function[1] : $function; + $this->_plugins['outputfilter'][$_name] + = array($function, null, null, false); + } + + /** + * Unregisters an outputfilter function + * + * @param string $function name of PHP function + */ + function unregister_outputfilter($function) + { + unset($this->_plugins['outputfilter'][$function]); + } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + */ + function load_filter($type, $name) + { + switch ($type) { + case 'output': + $_params = array('plugins' => array(array($type . 'filter', $name, null, null, false))); + require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); + smarty_core_load_plugins($_params, $this); + break; + + case 'pre': + case 'post': + if (!isset($this->_plugins[$type . 'filter'][$name])) + $this->_plugins[$type . 'filter'][$name] = false; + break; + } + } + + /** + * clear cached content for the given template and cache id + * + * @param string $tpl_file name of template file + * @param string $cache_id name of cache_id + * @param string $compile_id name of compile_id + * @param string $exp_time expiration time + * @return boolean + */ + function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null) + { + + if (!isset($compile_id)) + $compile_id = $this->compile_id; + + if (!isset($tpl_file)) + $compile_id = null; + + $_auto_id = $this->_get_auto_id($cache_id, $compile_id); + + if (!empty($this->cache_handler_func)) { + return call_user_func_array($this->cache_handler_func, + array('clear', &$this, &$dummy, $tpl_file, $cache_id, $compile_id, $exp_time)); + } else { + $_params = array('auto_base' => $this->cache_dir, + 'auto_source' => $tpl_file, + 'auto_id' => $_auto_id, + 'exp_time' => $exp_time); + require_once(SMARTY_CORE_DIR . 'core.rm_auto.php'); + return smarty_core_rm_auto($_params, $this); + } + + } + + + /** + * clear the entire contents of cache (all templates) + * + * @param string $exp_time expire time + * @return boolean results of {@link smarty_core_rm_auto()} + */ + function clear_all_cache($exp_time = null) + { + return $this->clear_cache(null, null, null, $exp_time); + } + + + /** + * test to see if valid cache exists for this template + * + * @param string $tpl_file name of template file + * @param string $cache_id + * @param string $compile_id + * @return string|false results of {@link _read_cache_file()} + */ + function is_cached($tpl_file, $cache_id = null, $compile_id = null) + { + if (!$this->caching) + return false; + + if (!isset($compile_id)) + $compile_id = $this->compile_id; + + $_params = array( + 'tpl_file' => $tpl_file, + 'cache_id' => $cache_id, + 'compile_id' => $compile_id + ); + require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php'); + return smarty_core_read_cache_file($_params, $this); + } + + + /** + * clear all the assigned template variables. + * + */ + function clear_all_assign() + { + $this->_tpl_vars = array(); + } + + /** + * clears compiled version of specified template resource, + * or all compiled template files if one is not specified. + * This function is for advanced use only, not normally needed. + * + * @param string $tpl_file + * @param string $compile_id + * @param string $exp_time + * @return boolean results of {@link smarty_core_rm_auto()} + */ + function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null) + { + if (!isset($compile_id)) { + $compile_id = $this->compile_id; + } + $_params = array('auto_base' => $this->compile_dir, + 'auto_source' => $tpl_file, + 'auto_id' => $compile_id, + 'exp_time' => $exp_time, + 'extensions' => array('.inc', '.php')); + require_once(SMARTY_CORE_DIR . 'core.rm_auto.php'); + return smarty_core_rm_auto($_params, $this); + } + + /** + * Checks whether requested template exists. + * + * @param string $tpl_file + * @return boolean + */ + function template_exists($tpl_file) + { + $_params = array('resource_name' => $tpl_file, 'quiet'=>true, 'get_source'=>false); + return $this->_fetch_resource_info($_params); + } + + /** + * Returns an array containing template variables + * + * @param string $name + * @param string $type + * @return array + */ + function &get_template_vars($name=null) + { + if(!isset($name)) { + return $this->_tpl_vars; + } elseif(isset($this->_tpl_vars[$name])) { + return $this->_tpl_vars[$name]; + } else { + // var non-existant, return valid reference + $_tmp = null; + return $_tmp; + } + } + + /** + * Returns an array containing config variables + * + * @param string $name + * @param string $type + * @return array + */ + function &get_config_vars($name=null) + { + if(!isset($name) && is_array($this->_config[0])) { + return $this->_config[0]['vars']; + } else if(isset($this->_config[0]['vars'][$name])) { + return $this->_config[0]['vars'][$name]; + } else { + // var non-existant, return valid reference + $_tmp = null; + return $_tmp; + } + } + + /** + * trigger Smarty error + * + * @param string $error_msg + * @param integer $error_type + */ + function trigger_error($error_msg, $error_type = E_USER_WARNING) + { + trigger_error("Smarty error: $error_msg", $error_type); + } + + + /** + * executes & displays the template results + * + * @param string $resource_name + * @param string $cache_id + * @param string $compile_id + */ + function display($resource_name, $cache_id = null, $compile_id = null) + { + $this->fetch($resource_name, $cache_id, $compile_id, true); + } + + /** + * executes & returns or displays the template results + * + * @param string $resource_name + * @param string $cache_id + * @param string $compile_id + * @param boolean $display + */ + function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false) + { + static $_cache_info = array(); + + $_smarty_old_error_level = $this->debugging ? error_reporting() : error_reporting(isset($this->error_reporting) + ? $this->error_reporting : error_reporting() & ~E_NOTICE); + + if (!$this->debugging && $this->debugging_ctrl == 'URL') { + $_query_string = $this->request_use_auto_globals ? $_SERVER['QUERY_STRING'] : $GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING']; + if (@strstr($_query_string, $this->_smarty_debug_id)) { + if (@strstr($_query_string, $this->_smarty_debug_id . '=on')) { + // enable debugging for this browser session + @setcookie('SMARTY_DEBUG', true); + $this->debugging = true; + } elseif (@strstr($_query_string, $this->_smarty_debug_id . '=off')) { + // disable debugging for this browser session + @setcookie('SMARTY_DEBUG', false); + $this->debugging = false; + } else { + // enable debugging for this page + $this->debugging = true; + } + } else { + $this->debugging = (bool)($this->request_use_auto_globals ? @$_COOKIE['SMARTY_DEBUG'] : @$GLOBALS['HTTP_COOKIE_VARS']['SMARTY_DEBUG']); + } + } + + if ($this->debugging) { + // capture time for debugging info + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $_debug_start_time = smarty_core_get_microtime($_params, $this); + $this->_smarty_debug_info[] = array('type' => 'template', + 'filename' => $resource_name, + 'depth' => 0); + $_included_tpls_idx = count($this->_smarty_debug_info) - 1; + } + + if (!isset($compile_id)) { + $compile_id = $this->compile_id; + } + + $this->_compile_id = $compile_id; + $this->_inclusion_depth = 0; + + if ($this->caching) { + // save old cache_info, initialize cache_info + array_push($_cache_info, $this->_cache_info); + $this->_cache_info = array(); + $_params = array( + 'tpl_file' => $resource_name, + 'cache_id' => $cache_id, + 'compile_id' => $compile_id, + 'results' => null + ); + require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php'); + if (smarty_core_read_cache_file($_params, $this)) { + $_smarty_results = $_params['results']; + if (!empty($this->_cache_info['insert_tags'])) { + $_params = array('plugins' => $this->_cache_info['insert_tags']); + require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); + smarty_core_load_plugins($_params, $this); + $_params = array('results' => $_smarty_results); + require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php'); + $_smarty_results = smarty_core_process_cached_inserts($_params, $this); + } + if (!empty($this->_cache_info['cache_serials'])) { + $_params = array('results' => $_smarty_results); + require_once(SMARTY_CORE_DIR . 'core.process_compiled_include.php'); + $_smarty_results = smarty_core_process_compiled_include($_params, $this); + } + + + if ($display) { + if ($this->debugging) + { + // capture time for debugging info + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $_debug_start_time; + require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php'); + $_smarty_results .= smarty_core_display_debug_console($_params, $this); + } + if ($this->cache_modified_check) { + $_server_vars = ($this->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS']; + $_last_modified_date = @substr($_server_vars['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_server_vars['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3); + $_gmt_mtime = gmdate('D, d M Y H:i:s', $this->_cache_info['timestamp']).' GMT'; + if (@count($this->_cache_info['insert_tags']) == 0 + && !$this->_cache_serials + && $_gmt_mtime == $_last_modified_date) { + if (php_sapi_name()=='cgi') + header('Status: 304 Not Modified'); + else + header('HTTP/1.1 304 Not Modified'); + + } else { + header('Last-Modified: '.$_gmt_mtime); + echo $_smarty_results; + } + } else { + echo $_smarty_results; + } + error_reporting($_smarty_old_error_level); + // restore initial cache_info + $this->_cache_info = array_pop($_cache_info); + return true; + } else { + error_reporting($_smarty_old_error_level); + // restore initial cache_info + $this->_cache_info = array_pop($_cache_info); + return $_smarty_results; + } + } else { + $this->_cache_info['template'][$resource_name] = true; + if ($this->cache_modified_check && $display) { + header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT'); + } + } + } + + // load filters that are marked as autoload + if (count($this->autoload_filters)) { + foreach ($this->autoload_filters as $_filter_type => $_filters) { + foreach ($_filters as $_filter) { + $this->load_filter($_filter_type, $_filter); + } + } + } + + $_smarty_compile_path = $this->_get_compile_path($resource_name); + + // if we just need to display the results, don't perform output + // buffering - for speed + $_cache_including = $this->_cache_including; + $this->_cache_including = false; + if ($display && !$this->caching && count($this->_plugins['outputfilter']) == 0) { + if ($this->_is_compiled($resource_name, $_smarty_compile_path) + || $this->_compile_resource($resource_name, $_smarty_compile_path)) + { + include($_smarty_compile_path); + } + } else { + ob_start(); + if ($this->_is_compiled($resource_name, $_smarty_compile_path) + || $this->_compile_resource($resource_name, $_smarty_compile_path)) + { + include($_smarty_compile_path); + } + $_smarty_results = ob_get_contents(); + ob_end_clean(); + + foreach ((array)$this->_plugins['outputfilter'] as $_output_filter) { + $_smarty_results = call_user_func_array($_output_filter[0], array($_smarty_results, &$this)); + } + } + + if ($this->caching) { + $_params = array('tpl_file' => $resource_name, + 'cache_id' => $cache_id, + 'compile_id' => $compile_id, + 'results' => $_smarty_results); + require_once(SMARTY_CORE_DIR . 'core.write_cache_file.php'); + smarty_core_write_cache_file($_params, $this); + require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php'); + $_smarty_results = smarty_core_process_cached_inserts($_params, $this); + + if ($this->_cache_serials) { + // strip nocache-tags from output + $_smarty_results = preg_replace('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!s' + ,'' + ,$_smarty_results); + } + // restore initial cache_info + $this->_cache_info = array_pop($_cache_info); + } + $this->_cache_including = $_cache_including; + + if ($display) { + if (isset($_smarty_results)) { echo $_smarty_results; } + if ($this->debugging) { + // capture time for debugging info + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = (smarty_core_get_microtime($_params, $this) - $_debug_start_time); + require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php'); + echo smarty_core_display_debug_console($_params, $this); + } + error_reporting($_smarty_old_error_level); + return; + } else { + error_reporting($_smarty_old_error_level); + if (isset($_smarty_results)) { return $_smarty_results; } + } + } + + /** + * load configuration values + * + * @param string $file + * @param string $section + * @param string $scope + */ + function config_load($file, $section = null, $scope = 'global') + { + require_once($this->_get_plugin_filepath('function', 'config_load')); + smarty_function_config_load(array('file' => $file, 'section' => $section, 'scope' => $scope), $this); + } + + /** + * return a reference to a registered object + * + * @param string $name + * @return object + */ + function &get_registered_object($name) { + if (!isset($this->_reg_objects[$name])) + $this->_trigger_fatal_error("'$name' is not a registered object"); + + if (!is_object($this->_reg_objects[$name][0])) + $this->_trigger_fatal_error("registered '$name' is not an object"); + + return $this->_reg_objects[$name][0]; + } + + /** + * clear configuration values + * + * @param string $var + */ + function clear_config($var = null) + { + if(!isset($var)) { + // clear all values + $this->_config = array(array('vars' => array(), + 'files' => array())); + } else { + unset($this->_config[0]['vars'][$var]); + } + } + + /** + * get filepath of requested plugin + * + * @param string $type + * @param string $name + * @return string|false + */ + function _get_plugin_filepath($type, $name) + { + $_params = array('type' => $type, 'name' => $name); + require_once(SMARTY_CORE_DIR . 'core.assemble_plugin_filepath.php'); + return smarty_core_assemble_plugin_filepath($_params, $this); + } + + /** + * test if resource needs compiling + * + * @param string $resource_name + * @param string $compile_path + * @return boolean + */ + function _is_compiled($resource_name, $compile_path) + { + if (!$this->force_compile && file_exists($compile_path)) { + if (!$this->compile_check) { + // no need to check compiled file + return true; + } else { + // get file source and timestamp + $_params = array('resource_name' => $resource_name, 'get_source'=>false); + if (!$this->_fetch_resource_info($_params)) { + return false; + } + if ($_params['resource_timestamp'] <= filemtime($compile_path)) { + // template not expired, no recompile + return true; + } else { + // compile template + return false; + } + } + } else { + // compiled template does not exist, or forced compile + return false; + } + } + + /** + * compile the template + * + * @param string $resource_name + * @param string $compile_path + * @return boolean + */ + function _compile_resource($resource_name, $compile_path) + { + + $_params = array('resource_name' => $resource_name); + if (!$this->_fetch_resource_info($_params)) { + return false; + } + + $_source_content = $_params['source_content']; + $_cache_include = substr($compile_path, 0, -4).'.inc'; + + if ($this->_compile_source($resource_name, $_source_content, $_compiled_content, $_cache_include)) { + // if a _cache_serial was set, we also have to write an include-file: + if ($this->_cache_include_info) { + require_once(SMARTY_CORE_DIR . 'core.write_compiled_include.php'); + smarty_core_write_compiled_include(array_merge($this->_cache_include_info, array('compiled_content'=>$_compiled_content, 'resource_name'=>$resource_name)), $this); + } + + $_params = array('compile_path'=>$compile_path, 'compiled_content' => $_compiled_content); + require_once(SMARTY_CORE_DIR . 'core.write_compiled_resource.php'); + smarty_core_write_compiled_resource($_params, $this); + + return true; + } else { + return false; + } + + } + + /** + * compile the given source + * + * @param string $resource_name + * @param string $source_content + * @param string $compiled_content + * @return boolean + */ + function _compile_source($resource_name, &$source_content, &$compiled_content, $cache_include_path=null) + { + if (file_exists(SMARTY_DIR . $this->compiler_file)) { + require_once(SMARTY_DIR . $this->compiler_file); + } else { + // use include_path + require_once($this->compiler_file); + } + + + $smarty_compiler = new $this->compiler_class; + + $smarty_compiler->template_dir = $this->template_dir; + $smarty_compiler->compile_dir = $this->compile_dir; + $smarty_compiler->plugins_dir = $this->plugins_dir; + $smarty_compiler->config_dir = $this->config_dir; + $smarty_compiler->force_compile = $this->force_compile; + $smarty_compiler->caching = $this->caching; + $smarty_compiler->php_handling = $this->php_handling; + $smarty_compiler->left_delimiter = $this->left_delimiter; + $smarty_compiler->right_delimiter = $this->right_delimiter; + $smarty_compiler->_version = $this->_version; + $smarty_compiler->security = $this->security; + $smarty_compiler->secure_dir = $this->secure_dir; + $smarty_compiler->security_settings = $this->security_settings; + $smarty_compiler->trusted_dir = $this->trusted_dir; + $smarty_compiler->use_sub_dirs = $this->use_sub_dirs; + $smarty_compiler->_reg_objects = &$this->_reg_objects; + $smarty_compiler->_plugins = &$this->_plugins; + $smarty_compiler->_tpl_vars = &$this->_tpl_vars; + $smarty_compiler->default_modifiers = $this->default_modifiers; + $smarty_compiler->compile_id = $this->_compile_id; + $smarty_compiler->_config = $this->_config; + $smarty_compiler->request_use_auto_globals = $this->request_use_auto_globals; + + if (isset($cache_include_path) && isset($this->_cache_serials[$cache_include_path])) { + $smarty_compiler->_cache_serial = $this->_cache_serials[$cache_include_path]; + } + $smarty_compiler->_cache_include = $cache_include_path; + + + $_results = $smarty_compiler->_compile_file($resource_name, $source_content, $compiled_content); + + if ($smarty_compiler->_cache_serial) { + $this->_cache_include_info = array( + 'cache_serial'=>$smarty_compiler->_cache_serial + ,'plugins_code'=>$smarty_compiler->_plugins_code + ,'include_file_path' => $cache_include_path); + + } else { + $this->_cache_include_info = null; + + } + + return $_results; + } + + /** + * Get the compile path for this resource + * + * @param string $resource_name + * @return string results of {@link _get_auto_filename()} + */ + function _get_compile_path($resource_name) + { + return $this->_get_auto_filename($this->compile_dir, $resource_name, + $this->_compile_id) . '.php'; + } + + /** + * fetch the template info. Gets timestamp, and source + * if get_source is true + * + * sets $source_content to the source of the template, and + * $resource_timestamp to its time stamp + * @param string $resource_name + * @param string $source_content + * @param integer $resource_timestamp + * @param boolean $get_source + * @param boolean $quiet + * @return boolean + */ + + function _fetch_resource_info(&$params) + { + if(!isset($params['get_source'])) { $params['get_source'] = true; } + if(!isset($params['quiet'])) { $params['quiet'] = false; } + + $_return = false; + $_params = array('resource_name' => $params['resource_name']) ; + if (isset($params['resource_base_path'])) + $_params['resource_base_path'] = $params['resource_base_path']; + else + $_params['resource_base_path'] = $this->template_dir; + + if ($this->_parse_resource_name($_params)) { + $_resource_type = $_params['resource_type']; + $_resource_name = $_params['resource_name']; + switch ($_resource_type) { + case 'file': + if ($params['get_source']) { + $params['source_content'] = $this->_read_file($_resource_name); + } + $params['resource_timestamp'] = filemtime($_resource_name); + $_return = is_file($_resource_name); + break; + + default: + // call resource functions to fetch the template source and timestamp + if ($params['get_source']) { + $_source_return = isset($this->_plugins['resource'][$_resource_type]) && + call_user_func_array($this->_plugins['resource'][$_resource_type][0][0], + array($_resource_name, &$params['source_content'], &$this)); + } else { + $_source_return = true; + } + + $_timestamp_return = isset($this->_plugins['resource'][$_resource_type]) && + call_user_func_array($this->_plugins['resource'][$_resource_type][0][1], + array($_resource_name, &$params['resource_timestamp'], &$this)); + + $_return = $_source_return && $_timestamp_return; + break; + } + } + + if (!$_return) { + // see if we can get a template with the default template handler + if (!empty($this->default_template_handler_func)) { + if (!is_callable($this->default_template_handler_func)) { + $this->trigger_error("default template handler function \"$this->default_template_handler_func\" doesn't exist."); + } else { + $_return = call_user_func_array( + $this->default_template_handler_func, + array($_params['resource_type'], $_params['resource_name'], &$params['source_content'], &$params['resource_timestamp'], &$this)); + } + } + } + + if (!$_return) { + if (!$params['quiet']) { + $this->trigger_error('unable to read resource: "' . $params['resource_name'] . '"'); + } + } else if ($_return && $this->security) { + require_once(SMARTY_CORE_DIR . 'core.is_secure.php'); + if (!smarty_core_is_secure($_params, $this)) { + if (!$params['quiet']) + $this->trigger_error('(secure mode) accessing "' . $params['resource_name'] . '" is not allowed'); + $params['source_content'] = null; + $params['resource_timestamp'] = null; + return false; + } + } + return $_return; + } + + + /** + * parse out the type and name from the resource + * + * @param string $resource_base_path + * @param string $resource_name + * @param string $resource_type + * @param string $resource_name + * @return boolean + */ + + function _parse_resource_name(&$params) + { + + // split tpl_path by the first colon + $_resource_name_parts = explode(':', $params['resource_name'], 2); + + if (count($_resource_name_parts) == 1) { + // no resource type given + $params['resource_type'] = $this->default_resource_type; + $params['resource_name'] = $_resource_name_parts[0]; + } else { + if(strlen($_resource_name_parts[0]) == 1) { + // 1 char is not resource type, but part of filepath + $params['resource_type'] = $this->default_resource_type; + $params['resource_name'] = $params['resource_name']; + } else { + $params['resource_type'] = $_resource_name_parts[0]; + $params['resource_name'] = $_resource_name_parts[1]; + } + } + + if ($params['resource_type'] == 'file') { + if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $params['resource_name'])) { + // relative pathname to $params['resource_base_path'] + // use the first directory where the file is found + foreach ((array)$params['resource_base_path'] as $_curr_path) { + $_fullpath = $_curr_path . DIRECTORY_SEPARATOR . $params['resource_name']; + if (file_exists($_fullpath) && is_file($_fullpath)) { + $params['resource_name'] = $_fullpath; + return true; + } + // didn't find the file, try include_path + $_params = array('file_path' => $_fullpath); + require_once(SMARTY_CORE_DIR . 'core.get_include_path.php'); + if(smarty_core_get_include_path($_params, $this)) { + $params['resource_name'] = $_params['new_file_path']; + return true; + } + } + return false; + } else { + /* absolute path */ + return file_exists($params['resource_name']); + } + } elseif (empty($this->_plugins['resource'][$params['resource_type']])) { + $_params = array('type' => $params['resource_type']); + require_once(SMARTY_CORE_DIR . 'core.load_resource_plugin.php'); + smarty_core_load_resource_plugin($_params, $this); + } + + return true; + } + + + /** + * Handle modifiers + * + * @param string|null $modifier_name + * @param array|null $map_array + * @return string result of modifiers + */ + function _run_mod_handler() + { + $_args = func_get_args(); + list($_modifier_name, $_map_array) = array_splice($_args, 0, 2); + list($_func_name, $_tpl_file, $_tpl_line) = + $this->_plugins['modifier'][$_modifier_name]; + + $_var = $_args[0]; + foreach ($_var as $_key => $_val) { + $_args[0] = $_val; + $_var[$_key] = call_user_func_array($_func_name, $_args); + } + return $_var; + } + + /** + * Remove starting and ending quotes from the string + * + * @param string $string + * @return string + */ + function _dequote($string) + { + if ((substr($string, 0, 1) == "'" || substr($string, 0, 1) == '"') && + substr($string, -1) == substr($string, 0, 1)) + return substr($string, 1, -1); + else + return $string; + } + + + /** + * read in a file + * + * @param string $filename + * @return string + */ + function _read_file($filename) + { + if ( file_exists($filename) && ($fd = @fopen($filename, 'rb')) ) { + $contents = ($size = filesize($filename)) ? fread($fd, $size) : ''; + fclose($fd); + return $contents; + } else { + return false; + } + } + + /** + * get a concrete filename for automagically created content + * + * @param string $auto_base + * @param string $auto_source + * @param string $auto_id + * @return string + * @staticvar string|null + * @staticvar string|null + */ + function _get_auto_filename($auto_base, $auto_source = null, $auto_id = null) + { + $_compile_dir_sep = $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^'; + $_return = $auto_base . DIRECTORY_SEPARATOR; + + if(isset($auto_id)) { + // make auto_id safe for directory names + $auto_id = str_replace('%7C',$_compile_dir_sep,(urlencode($auto_id))); + // split into separate directories + $_return .= $auto_id . $_compile_dir_sep; + } + + if(isset($auto_source)) { + // make source name safe for filename + $_filename = urlencode(basename($auto_source)); + $_crc32 = sprintf('%08X', crc32($auto_source)); + // prepend %% to avoid name conflicts with + // with $params['auto_id'] names + $_crc32 = substr($_crc32, 0, 2) . $_compile_dir_sep . + substr($_crc32, 0, 3) . $_compile_dir_sep . $_crc32; + $_return .= '%%' . $_crc32 . '%%' . $_filename; + } + + return $_return; + } + + /** + * unlink a file, possibly using expiration time + * + * @param string $resource + * @param integer $exp_time + */ + function _unlink($resource, $exp_time = null) + { + if(isset($exp_time)) { + if(time() - @filemtime($resource) >= $exp_time) { + return @unlink($resource); + } + } else { + return @unlink($resource); + } + } + + /** + * returns an auto_id for auto-file-functions + * + * @param string $cache_id + * @param string $compile_id + * @return string|null + */ + function _get_auto_id($cache_id=null, $compile_id=null) { + if (isset($cache_id)) + return (isset($compile_id)) ? $cache_id . '|' . $compile_id : $cache_id; + elseif(isset($compile_id)) + return $compile_id; + else + return null; + } + + /** + * trigger Smarty plugin error + * + * @param string $error_msg + * @param string $tpl_file + * @param integer $tpl_line + * @param string $file + * @param integer $line + * @param integer $error_type + */ + function _trigger_fatal_error($error_msg, $tpl_file = null, $tpl_line = null, + $file = null, $line = null, $error_type = E_USER_ERROR) + { + if(isset($file) && isset($line)) { + $info = ' ('.basename($file).", line $line)"; + } else { + $info = ''; + } + if (isset($tpl_line) && isset($tpl_file)) { + $this->trigger_error('[in ' . $tpl_file . ' line ' . $tpl_line . "]: $error_msg$info", $error_type); + } else { + $this->trigger_error($error_msg . $info, $error_type); + } + } + + + /** + * callback function for preg_replace, to call a non-cacheable block + * @return string + */ + function _process_compiled_include_callback($match) { + $_func = '_smarty_tplfunc_'.$match[2].'_'.$match[3]; + ob_start(); + $_func($this); + $_ret = ob_get_contents(); + ob_end_clean(); + return $_ret; + } + + + /** + * called for included templates + * + * @param string $_smarty_include_tpl_file + * @param string $_smarty_include_vars + */ + + // $_smarty_include_tpl_file, $_smarty_include_vars + + function _smarty_include($params) + { + if ($this->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $debug_start_time = smarty_core_get_microtime($_params, $this); + $this->_smarty_debug_info[] = array('type' => 'template', + 'filename' => $params['smarty_include_tpl_file'], + 'depth' => ++$this->_inclusion_depth); + $included_tpls_idx = count($this->_smarty_debug_info) - 1; + } + + $this->_tpl_vars = array_merge($this->_tpl_vars, $params['smarty_include_vars']); + + // config vars are treated as local, so push a copy of the + // current ones onto the front of the stack + array_unshift($this->_config, $this->_config[0]); + + $_smarty_compile_path = $this->_get_compile_path($params['smarty_include_tpl_file']); + + + if ($this->_is_compiled($params['smarty_include_tpl_file'], $_smarty_compile_path) + || $this->_compile_resource($params['smarty_include_tpl_file'], $_smarty_compile_path)) + { + include($_smarty_compile_path); + } + + // pop the local vars off the front of the stack + array_shift($this->_config); + + $this->_inclusion_depth--; + + if ($this->debugging) { + // capture time for debugging info + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $this->_smarty_debug_info[$included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $debug_start_time; + } + + if ($this->caching) { + $this->_cache_info['template'][$params['smarty_include_tpl_file']] = true; + } + } + + + /** + * get or set an array of cached attributes for function that is + * not cacheable + * @return array + */ + function &_smarty_cache_attrs($cache_serial, $count) { + $_cache_attrs =& $this->_cache_info['cache_attrs'][$cache_serial][$count]; + + if ($this->_cache_including) { + /* return next set of cache_attrs */ + $_return = current($_cache_attrs); + next($_cache_attrs); + return $_return; + + } else { + /* add a reference to a new set of cache_attrs */ + $_cache_attrs[] = array(); + return $_cache_attrs[count($_cache_attrs)-1]; + + } + + } + + + /** + * wrapper for include() retaining $this + * @return mixed + */ + function _include($filename, $once=false, $params=null) + { + if ($once) { + return include_once($filename); + } else { + return include($filename); + } + } + + + /** + * wrapper for eval() retaining $this + * @return mixed + */ + function _eval($code, $params=null) + { + return eval($code); + } + /**#@-*/ + +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/Smarty_Compiler.class.php b/library/smarty/Smarty_Compiler.class.php new file mode 100755 index 0000000..1af1e57 --- /dev/null +++ b/library/smarty/Smarty_Compiler.class.php @@ -0,0 +1,2311 @@ + + * @author Andrei Zmievski + * @version 2.6.11 + * @copyright 2001-2005 New Digital Group, Inc. + * @package Smarty + */ + +/* $Id: Smarty_Compiler.class.php,v 1.1.1.1 2006/04/26 20:43:08 bsimpson Exp $ */ + +/** + * Template compiling class + * @package Smarty + */ +class Smarty_Compiler extends Smarty { + + // internal vars + /**#@+ + * @access private + */ + var $_folded_blocks = array(); // keeps folded template blocks + var $_current_file = null; // the current template being compiled + var $_current_line_no = 1; // line number for error messages + var $_capture_stack = array(); // keeps track of nested capture buffers + var $_plugin_info = array(); // keeps track of plugins to load + var $_init_smarty_vars = false; + var $_permitted_tokens = array('true','false','yes','no','on','off','null'); + var $_db_qstr_regexp = null; // regexps are setup in the constructor + var $_si_qstr_regexp = null; + var $_qstr_regexp = null; + var $_func_regexp = null; + var $_reg_obj_regexp = null; + var $_var_bracket_regexp = null; + var $_num_const_regexp = null; + var $_dvar_guts_regexp = null; + var $_dvar_regexp = null; + var $_cvar_regexp = null; + var $_svar_regexp = null; + var $_avar_regexp = null; + var $_mod_regexp = null; + var $_var_regexp = null; + var $_parenth_param_regexp = null; + var $_func_call_regexp = null; + var $_obj_ext_regexp = null; + var $_obj_start_regexp = null; + var $_obj_params_regexp = null; + var $_obj_call_regexp = null; + var $_cacheable_state = 0; + var $_cache_attrs_count = 0; + var $_nocache_count = 0; + var $_cache_serial = null; + var $_cache_include = null; + + var $_strip_depth = 0; + var $_additional_newline = "\n"; + + /**#@-*/ + /** + * The class constructor. + */ + function Smarty_Compiler() + { + // matches double quoted strings: + // "foobar" + // "foo\"bar" + $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; + + // matches single quoted strings: + // 'foobar' + // 'foo\'bar' + $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; + + // matches single or double quoted strings + $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')'; + + // matches bracket portion of vars + // [0] + // [foo] + // [$bar] + $this->_var_bracket_regexp = '\[\$?[\w\.]+\]'; + + // matches numerical constants + // 30 + // -12 + // 13.22 + $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)'; + + // matches $ vars (not objects): + // $foo + // $foo.bar + // $foo.bar.foobar + // $foo[0] + // $foo[$bar] + // $foo[5][blah] + // $foo[5].bar[$foobar][4] + $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))'; + $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]'; + $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp + . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?'; + $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp; + + // matches config vars: + // #foo# + // #foobar123_foo# + $this->_cvar_regexp = '\#\w+\#'; + + // matches section vars: + // %foo.bar% + $this->_svar_regexp = '\%\w+\.\w+\%'; + + // matches all valid variables (no quotes, no modifiers) + $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' + . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')'; + + // matches valid variable syntax: + // $foo + // $foo + // #foo# + // #foo# + // "text" + // "text" + $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')'; + + // matches valid object call (one level of object nesting allowed in parameters): + // $foo->bar + // $foo->bar() + // $foo->bar("text") + // $foo->bar($foo, $bar, "text") + // $foo->bar($foo, "foo") + // $foo->bar->foo() + // $foo->bar->foo->bar() + // $foo->bar($foo->bar) + // $foo->bar($foo->bar()) + // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar)) + $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')'; + $this->_obj_restricted_param_regexp = '(?:' + . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')' + . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)'; + $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|' + . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)'; + $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp + . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)'; + $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)'; + $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)'; + + // matches valid modifier syntax: + // |foo + // |@foo + // |foo:"bar" + // |foo:$bar + // |foo:"bar":$foobar + // |foo|bar + // |foo:$foo->bar + $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|' + . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)'; + + // matches valid function name: + // foo123 + // _foo_bar + $this->_func_regexp = '[a-zA-Z_]\w*'; + + // matches valid registered object: + // foo->bar + $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*'; + + // matches valid parameter values: + // true + // $foo + // $foo|bar + // #foo# + // #foo#|bar + // "text" + // "text"|bar + // $foo->bar + $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|' + . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)'; + + // matches valid parenthesised function parameters: + // + // "text" + // $foo, $bar, "text" + // $foo|bar, "foo"|bar, $foo->bar($foo)|bar + $this->_parenth_param_regexp = '(?:\((?:\w+|' + . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|' + . $this->_param_regexp . ')))*)?\))'; + + // matches valid function call: + // foo() + // foo_bar($foo) + // _foo_bar($foo,"bar") + // foo123($foo,$foo->bar(),"foo") + $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:' + . $this->_parenth_param_regexp . '))'; + } + + /** + * compile a resource + * + * sets $compiled_content to the compiled source + * @param string $resource_name + * @param string $source_content + * @param string $compiled_content + * @return true + */ + function _compile_file($resource_name, $source_content, &$compiled_content) + { + + if ($this->security) { + // do not allow php syntax to be executed unless specified + if ($this->php_handling == SMARTY_PHP_ALLOW && + !$this->security_settings['PHP_HANDLING']) { + $this->php_handling = SMARTY_PHP_PASSTHRU; + } + } + + $this->_load_filters(); + + $this->_current_file = $resource_name; + $this->_current_line_no = 1; + $ldq = preg_quote($this->left_delimiter, '~'); + $rdq = preg_quote($this->right_delimiter, '~'); + + // run template source through prefilter functions + if (count($this->_plugins['prefilter']) > 0) { + foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { + if ($prefilter === false) continue; + if ($prefilter[3] || is_callable($prefilter[0])) { + $source_content = call_user_func_array($prefilter[0], + array($source_content, &$this)); + $this->_plugins['prefilter'][$filter_name][3] = true; + } else { + $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented"); + } + } + } + + /* fetch all special blocks */ + $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s"; + + preg_match_all($search, $source_content, $match, PREG_SET_ORDER); + $this->_folded_blocks = $match; + reset($this->_folded_blocks); + + /* replace special blocks by "{php}" */ + $source_content = preg_replace($search.'e', "'" + . $this->_quote_replace($this->left_delimiter) . 'php' + . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'" + . $this->_quote_replace($this->right_delimiter) + . "'" + , $source_content); + + /* Gather all template tags. */ + preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match); + $template_tags = $_match[1]; + /* Split content by template tags to obtain non-template content. */ + $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content); + + /* loop through text blocks */ + for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) { + /* match anything resembling php tags */ + if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) { + /* replace tags with placeholders to prevent recursive replacements */ + $sp_match[1] = array_unique($sp_match[1]); + usort($sp_match[1], '_smarty_sort_length'); + for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { + $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]); + } + /* process each one */ + for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) { + if ($this->php_handling == SMARTY_PHP_PASSTHRU) { + /* echo php contents */ + $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', ''."\n", $text_blocks[$curr_tb]); + } else if ($this->php_handling == SMARTY_PHP_QUOTE) { + /* quote php tags */ + $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]); + } else if ($this->php_handling == SMARTY_PHP_REMOVE) { + /* remove php tags */ + $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]); + } else { + /* SMARTY_PHP_ALLOW, but echo non php starting tags */ + $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', ''."\n", $sp_match[1][$curr_sp]); + $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]); + } + } + } + } + + /* Compile the template tags into PHP code. */ + $compiled_tags = array(); + for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) { + $this->_current_line_no += substr_count($text_blocks[$i], "\n"); + $compiled_tags[] = $this->_compile_tag($template_tags[$i]); + $this->_current_line_no += substr_count($template_tags[$i], "\n"); + } + if (count($this->_tag_stack)>0) { + list($_open_tag, $_line_no) = end($this->_tag_stack); + $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__); + return; + } + + /* Reformat $text_blocks between 'strip' and '/strip' tags, + removing spaces, tabs and newlines. */ + $strip = false; + for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { + if ($compiled_tags[$i] == '{strip}') { + $compiled_tags[$i] = ''; + $strip = true; + /* remove leading whitespaces */ + $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]); + } + if ($strip) { + /* strip all $text_blocks before the next '/strip' */ + for ($j = $i + 1; $j < $for_max; $j++) { + /* remove leading and trailing whitespaces of each line */ + $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]); + if ($compiled_tags[$j] == '{/strip}') { + /* remove trailing whitespaces from the last text_block */ + $text_blocks[$j] = rtrim($text_blocks[$j]); + } + $text_blocks[$j] = ""\'", "\\"=>"\\\\")) . "'; ?>"; + if ($compiled_tags[$j] == '{/strip}') { + $compiled_tags[$j] = "\n"; /* slurped by php, but necessary + if a newline is following the closing strip-tag */ + $strip = false; + $i = $j; + break; + } + } + } + } + $compiled_content = ''; + + /* Interleave the compiled contents and text blocks to get the final result. */ + for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) { + if ($compiled_tags[$i] == '') { + // tag result empty, remove first newline from following text block + $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]); + } + $compiled_content .= $text_blocks[$i].$compiled_tags[$i]; + } + $compiled_content .= $text_blocks[$i]; + + // remove \n from the end of the file, if any + if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) { + $compiled_content = substr($compiled_content, 0, -1); + } + + if (!empty($this->_cache_serial)) { + $compiled_content = "_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content; + } + + // remove unnecessary close/open tags + $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content); + + // run compiled template through postfilter functions + if (count($this->_plugins['postfilter']) > 0) { + foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { + if ($postfilter === false) continue; + if ($postfilter[3] || is_callable($postfilter[0])) { + $compiled_content = call_user_func_array($postfilter[0], + array($compiled_content, &$this)); + $this->_plugins['postfilter'][$filter_name][3] = true; + } else { + $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented"); + } + } + } + + // put header at the top of the compiled template + $template_header = "_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; + $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n"; + + /* Emit code to load needed plugins. */ + $this->_plugins_code = ''; + if (count($this->_plugin_info)) { + $_plugins_params = "array('plugins' => array("; + foreach ($this->_plugin_info as $plugin_type => $plugins) { + foreach ($plugins as $plugin_name => $plugin_info) { + $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], "; + $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),'; + } + } + $_plugins_params .= '))'; + $plugins_code = "\n"; + $template_header .= $plugins_code; + $this->_plugin_info = array(); + $this->_plugins_code = $plugins_code; + } + + if ($this->_init_smarty_vars) { + $template_header .= "\n"; + $this->_init_smarty_vars = false; + } + + $compiled_content = $template_header . $compiled_content; + return true; + } + + /** + * Compile a template tag + * + * @param string $template_tag + * @return string + */ + function _compile_tag($template_tag) + { + /* Matched comment. */ + if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*') + return ''; + + /* Split tag into two three parts: command, command modifiers and the arguments. */ + if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp + . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)) + (?:\s+(.*))?$ + ~xs', $template_tag, $match)) { + $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__); + } + + $tag_command = $match[1]; + $tag_modifier = isset($match[2]) ? $match[2] : null; + $tag_args = isset($match[3]) ? $match[3] : null; + + if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) { + /* tag name is a variable or object */ + $_return = $this->_parse_var_props($tag_command . $tag_modifier); + return "" . $this->_additional_newline; + } + + /* If the tag name is a registered object, we process it. */ + if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) { + return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier); + } + + switch ($tag_command) { + case 'include': + return $this->_compile_include_tag($tag_args); + + case 'include_php': + return $this->_compile_include_php_tag($tag_args); + + case 'if': + $this->_push_tag('if'); + return $this->_compile_if_tag($tag_args); + + case 'else': + list($_open_tag) = end($this->_tag_stack); + if ($_open_tag != 'if' && $_open_tag != 'elseif') + $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__); + else + $this->_push_tag('else'); + return ''; + + case 'elseif': + list($_open_tag) = end($this->_tag_stack); + if ($_open_tag != 'if' && $_open_tag != 'elseif') + $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__); + if ($_open_tag == 'if') + $this->_push_tag('elseif'); + return $this->_compile_if_tag($tag_args, true); + + case '/if': + $this->_pop_tag('if'); + return ''; + + case 'capture': + return $this->_compile_capture_tag(true, $tag_args); + + case '/capture': + return $this->_compile_capture_tag(false); + + case 'ldelim': + return $this->left_delimiter; + + case 'rdelim': + return $this->right_delimiter; + + case 'section': + $this->_push_tag('section'); + return $this->_compile_section_start($tag_args); + + case 'sectionelse': + $this->_push_tag('sectionelse'); + return ""; + break; + + case '/section': + $_open_tag = $this->_pop_tag('section'); + if ($_open_tag == 'sectionelse') + return ""; + else + return ""; + + case 'foreach': + $this->_push_tag('foreach'); + return $this->_compile_foreach_start($tag_args); + break; + + case 'foreachelse': + $this->_push_tag('foreachelse'); + return ""; + + case '/foreach': + $_open_tag = $this->_pop_tag('foreach'); + if ($_open_tag == 'foreachelse') + return ""; + else + return ""; + break; + + case 'strip': + case '/strip': + if (substr($tag_command, 0, 1)=='/') { + $this->_pop_tag('strip'); + if (--$this->_strip_depth==0) { /* outermost closing {/strip} */ + $this->_additional_newline = "\n"; + return '{' . $tag_command . '}'; + } + } else { + $this->_push_tag('strip'); + if ($this->_strip_depth++==0) { /* outermost opening {strip} */ + $this->_additional_newline = ""; + return '{' . $tag_command . '}'; + } + } + return ''; + + case 'php': + /* handle folded tags replaced by {php} */ + list(, $block) = each($this->_folded_blocks); + $this->_current_line_no += substr_count($block[0], "\n"); + /* the number of matched elements in the regexp in _compile_file() + determins the type of folded tag that was found */ + switch (count($block)) { + case 2: /* comment */ + return ''; + + case 3: /* literal */ + return ""\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline; + + case 4: /* php */ + if ($this->security && !$this->security_settings['PHP_TAGS']) { + $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__); + return; + } + return ''; + } + break; + + case 'insert': + return $this->_compile_insert_tag($tag_args); + + default: + if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) { + return $output; + } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) { + return $output; + } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) { + return $output; + } else { + $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__); + } + + } + } + + + /** + * compile the custom compiler tag + * + * sets $output to the compiled custom compiler tag + * @param string $tag_command + * @param string $tag_args + * @param string $output + * @return boolean + */ + function _compile_compiler_tag($tag_command, $tag_args, &$output) + { + $found = false; + $have_function = true; + + /* + * First we check if the compiler function has already been registered + * or loaded from a plugin file. + */ + if (isset($this->_plugins['compiler'][$tag_command])) { + $found = true; + $plugin_func = $this->_plugins['compiler'][$tag_command][0]; + if (!is_callable($plugin_func)) { + $message = "compiler function '$tag_command' is not implemented"; + $have_function = false; + } + } + /* + * Otherwise we need to load plugin file and look for the function + * inside it. + */ + else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) { + $found = true; + + include_once $plugin_file; + + $plugin_func = 'smarty_compiler_' . $tag_command; + if (!is_callable($plugin_func)) { + $message = "plugin function $plugin_func() not found in $plugin_file\n"; + $have_function = false; + } else { + $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true); + } + } + + /* + * True return value means that we either found a plugin or a + * dynamically registered function. False means that we didn't and the + * compiler should now emit code to load custom function plugin for this + * tag. + */ + if ($found) { + if ($have_function) { + $output = call_user_func_array($plugin_func, array($tag_args, &$this)); + if($output != '') { + $output = '_push_cacheable_state('compiler', $tag_command) + . $output + . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>'; + } + } else { + $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); + } + return true; + } else { + return false; + } + } + + + /** + * compile block function tag + * + * sets $output to compiled block function tag + * @param string $tag_command + * @param string $tag_args + * @param string $tag_modifier + * @param string $output + * @return boolean + */ + function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output) + { + if (substr($tag_command, 0, 1) == '/') { + $start_tag = false; + $tag_command = substr($tag_command, 1); + } else + $start_tag = true; + + $found = false; + $have_function = true; + + /* + * First we check if the block function has already been registered + * or loaded from a plugin file. + */ + if (isset($this->_plugins['block'][$tag_command])) { + $found = true; + $plugin_func = $this->_plugins['block'][$tag_command][0]; + if (!is_callable($plugin_func)) { + $message = "block function '$tag_command' is not implemented"; + $have_function = false; + } + } + /* + * Otherwise we need to load plugin file and look for the function + * inside it. + */ + else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) { + $found = true; + + include_once $plugin_file; + + $plugin_func = 'smarty_block_' . $tag_command; + if (!function_exists($plugin_func)) { + $message = "plugin function $plugin_func() not found in $plugin_file\n"; + $have_function = false; + } else { + $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true); + + } + } + + if (!$found) { + return false; + } else if (!$have_function) { + $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); + return true; + } + + /* + * Even though we've located the plugin function, compilation + * happens only once, so the plugin will still need to be loaded + * at runtime for future requests. + */ + $this->_add_plugin('block', $tag_command); + + if ($start_tag) + $this->_push_tag($tag_command); + else + $this->_pop_tag($tag_command); + + if ($start_tag) { + $output = '_push_cacheable_state('block', $tag_command); + $attrs = $this->_parse_attrs($tag_args); + $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs=''); + $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); '; + $output .= $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat=true);'; + $output .= 'while ($_block_repeat) { ob_start(); ?>'; + } else { + $output = '_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat=false)'; + if ($tag_modifier != '') { + $this->_parse_modifiers($_out_tag_text, $tag_modifier); + } + $output .= 'echo '.$_out_tag_text.'; } '; + $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>'; + } + + return true; + } + + + /** + * compile custom function tag + * + * @param string $tag_command + * @param string $tag_args + * @param string $tag_modifier + * @return string + */ + function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output) + { + $found = false; + $have_function = true; + + /* + * First we check if the custom function has already been registered + * or loaded from a plugin file. + */ + if (isset($this->_plugins['function'][$tag_command])) { + $found = true; + $plugin_func = $this->_plugins['function'][$tag_command][0]; + if (!is_callable($plugin_func)) { + $message = "custom function '$tag_command' is not implemented"; + $have_function = false; + } + } + /* + * Otherwise we need to load plugin file and look for the function + * inside it. + */ + else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) { + $found = true; + + include_once $plugin_file; + + $plugin_func = 'smarty_function_' . $tag_command; + if (!function_exists($plugin_func)) { + $message = "plugin function $plugin_func() not found in $plugin_file\n"; + $have_function = false; + } else { + $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true); + + } + } + + if (!$found) { + return false; + } else if (!$have_function) { + $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__); + return true; + } + + /* declare plugin to be loaded on display of the template that + we compile right now */ + $this->_add_plugin('function', $tag_command); + + $_cacheable_state = $this->_push_cacheable_state('function', $tag_command); + $attrs = $this->_parse_attrs($tag_args); + $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs=''); + + $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)"; + if($tag_modifier != '') { + $this->_parse_modifiers($output, $tag_modifier); + } + + if($output != '') { + $output = '_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline; + } + + return true; + } + + /** + * compile a registered object tag + * + * @param string $tag_command + * @param array $attrs + * @param string $tag_modifier + * @return string + */ + function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) + { + if (substr($tag_command, 0, 1) == '/') { + $start_tag = false; + $tag_command = substr($tag_command, 1); + } else { + $start_tag = true; + } + + list($object, $obj_comp) = explode('->', $tag_command); + + $arg_list = array(); + if(count($attrs)) { + $_assign_var = false; + foreach ($attrs as $arg_name => $arg_value) { + if($arg_name == 'assign') { + $_assign_var = $arg_value; + unset($attrs['assign']); + continue; + } + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; + $arg_list[] = "'$arg_name' => $arg_value"; + } + } + + if($this->_reg_objects[$object][2]) { + // smarty object argument format + $args = "array(".implode(',', (array)$arg_list)."), \$this"; + } else { + // traditional argument format + $args = implode(',', array_values($attrs)); + if (empty($args)) { + $args = 'null'; + } + } + + $prefix = ''; + $postfix = ''; + $newline = ''; + if(!is_object($this->_reg_objects[$object][0])) { + $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); + } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) { + $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); + } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) { + // method + if(in_array($obj_comp, $this->_reg_objects[$object][3])) { + // block method + if ($start_tag) { + $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); "; + $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); "; + $prefix .= "while (\$_block_repeat) { ob_start();"; + $return = null; + $postfix = ''; + } else { + $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); "; + $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat=false)"; + $postfix = "} array_pop(\$this->_tag_stack);"; + } + } else { + // non-block method + $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)"; + } + } else { + // property + $return = "\$this->_reg_objects['$object'][0]->$obj_comp"; + } + + if($return != null) { + if($tag_modifier != '') { + $this->_parse_modifiers($return, $tag_modifier); + } + + if(!empty($_assign_var)) { + $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);"; + } else { + $output = 'echo ' . $return . ';'; + $newline = $this->_additional_newline; + } + } else { + $output = ''; + } + + return '" . $newline; + } + + /** + * Compile {insert ...} tag + * + * @param string $tag_args + * @return string + */ + function _compile_insert_tag($tag_args) + { + $attrs = $this->_parse_attrs($tag_args); + $name = $this->_dequote($attrs['name']); + + if (empty($name)) { + $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__); + } + + if (!empty($attrs['script'])) { + $delayed_loading = true; + } else { + $delayed_loading = false; + } + + foreach ($attrs as $arg_name => $arg_value) { + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; + $arg_list[] = "'$arg_name' => $arg_value"; + } + + $this->_add_plugin('insert', $name, $delayed_loading); + + $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))"; + + return "" . $this->_additional_newline; + } + + /** + * Compile {include ...} tag + * + * @param string $tag_args + * @return string + */ + function _compile_include_tag($tag_args) + { + $attrs = $this->_parse_attrs($tag_args); + $arg_list = array(); + + if (empty($attrs['file'])) { + $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__); + } + + foreach ($attrs as $arg_name => $arg_value) { + if ($arg_name == 'file') { + $include_file = $arg_value; + continue; + } else if ($arg_name == 'assign') { + $assign_var = $arg_value; + continue; + } + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; + $arg_list[] = "'$arg_name' => $arg_value"; + } + + $output = '_tpl_vars;\n"; + + + $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))"; + $output .= "\$this->_smarty_include($_params);\n" . + "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" . + "unset(\$_smarty_tpl_vars);\n"; + + if (isset($assign_var)) { + $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n"; + } + + $output .= ' ?>'; + + return $output; + + } + + /** + * Compile {include ...} tag + * + * @param string $tag_args + * @return string + */ + function _compile_include_php_tag($tag_args) + { + $attrs = $this->_parse_attrs($tag_args); + + if (empty($attrs['file'])) { + $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__); + } + + $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']); + $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true'; + + $arg_list = array(); + foreach($attrs as $arg_name => $arg_value) { + if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') { + if(is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; + $arg_list[] = "'$arg_name' => $arg_value"; + } + } + + $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))"; + + return "" . $this->_additional_newline; + } + + + /** + * Compile {section ...} tag + * + * @param string $tag_args + * @return string + */ + function _compile_section_start($tag_args) + { + $attrs = $this->_parse_attrs($tag_args); + $arg_list = array(); + + $output = '_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__); + } + + $output .= "unset(\$this->_sections[$section_name]);\n"; + $section_props = "\$this->_sections[$section_name]"; + + foreach ($attrs as $attr_name => $attr_value) { + switch ($attr_name) { + case 'loop': + $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n"; + break; + + case 'show': + if (is_bool($attr_value)) + $show_attr_value = $attr_value ? 'true' : 'false'; + else + $show_attr_value = "(bool)$attr_value"; + $output .= "{$section_props}['show'] = $show_attr_value;\n"; + break; + + case 'name': + $output .= "{$section_props}['$attr_name'] = $attr_value;\n"; + break; + + case 'max': + case 'start': + $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n"; + break; + + case 'step': + $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n"; + break; + + default: + $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__); + break; + } + } + + if (!isset($attrs['show'])) + $output .= "{$section_props}['show'] = true;\n"; + + if (!isset($attrs['loop'])) + $output .= "{$section_props}['loop'] = 1;\n"; + + if (!isset($attrs['max'])) + $output .= "{$section_props}['max'] = {$section_props}['loop'];\n"; + else + $output .= "if ({$section_props}['max'] < 0)\n" . + " {$section_props}['max'] = {$section_props}['loop'];\n"; + + if (!isset($attrs['step'])) + $output .= "{$section_props}['step'] = 1;\n"; + + if (!isset($attrs['start'])) + $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n"; + else { + $output .= "if ({$section_props}['start'] < 0)\n" . + " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" . + "else\n" . + " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n"; + } + + $output .= "if ({$section_props}['show']) {\n"; + if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) { + $output .= " {$section_props}['total'] = {$section_props}['loop'];\n"; + } else { + $output .= " {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n"; + } + $output .= " if ({$section_props}['total'] == 0)\n" . + " {$section_props}['show'] = false;\n" . + "} else\n" . + " {$section_props}['total'] = 0;\n"; + + $output .= "if ({$section_props}['show']):\n"; + $output .= " + for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1; + {$section_props}['iteration'] <= {$section_props}['total']; + {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n"; + $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n"; + $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n"; + $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n"; + $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n"; + $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n"; + + $output .= "?>"; + + return $output; + } + + + /** + * Compile {foreach ...} tag. + * + * @param string $tag_args + * @return string + */ + function _compile_foreach_start($tag_args) + { + $attrs = $this->_parse_attrs($tag_args); + $arg_list = array(); + + if (empty($attrs['from'])) { + return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__); + } + $from = $attrs['from']; + + if (empty($attrs['item'])) { + return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__); + } + $item = $this->_dequote($attrs['item']); + if (!preg_match('~^\w+$~', $item)) { + return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); + } + + if (isset($attrs['key'])) { + $key = $this->_dequote($attrs['key']); + if (!preg_match('~^\w+$~', $key)) { + return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__); + } + $key_part = "\$this->_tpl_vars['$key'] => "; + } else { + $key = null; + $key_part = ''; + } + + if (isset($attrs['name'])) { + $name = $attrs['name']; + } else { + $name = null; + } + + $output = '_foreach[$name]"; + $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n"; + $output .= "if ({$foreach_props}['total'] > 0):\n"; + $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; + $output .= " {$foreach_props}['iteration']++;\n"; + } else { + $output .= "if (count(\$_from)):\n"; + $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n"; + } + $output .= '?>'; + + return $output; + } + + + /** + * Compile {capture} .. {/capture} tags + * + * @param boolean $start true if this is the {capture} tag + * @param string $tag_args + * @return string + */ + + function _compile_capture_tag($start, $tag_args = '') + { + $attrs = $this->_parse_attrs($tag_args); + + if ($start) { + if (isset($attrs['name'])) + $buffer = $attrs['name']; + else + $buffer = "'default'"; + + if (isset($attrs['assign'])) + $assign = $attrs['assign']; + else + $assign = null; + $output = ""; + $this->_capture_stack[] = array($buffer, $assign); + } else { + list($buffer, $assign) = array_pop($this->_capture_stack); + $output = "_smarty_vars['capture'][$buffer] = ob_get_contents(); "; + if (isset($assign)) { + $output .= " \$this->assign($assign, ob_get_contents());"; + } + $output .= "ob_end_clean(); ?>"; + } + + return $output; + } + + /** + * Compile {if ...} tag + * + * @param string $tag_args + * @param boolean $elseif if true, uses elseif instead of if + * @return string + */ + function _compile_if_tag($tag_args, $elseif = false) + { + + /* Tokenize args for 'if' tag. */ + preg_match_all('~(?> + ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call + ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string + \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token + \b\w+\b | # valid word token + \S+ # anything else + )~x', $tag_args, $match); + + $tokens = $match[0]; + + if(empty($tokens)) { + $_error_msg .= $elseif ? "'elseif'" : "'if'"; + $_error_msg .= ' statement requires arguments'; + $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__); + } + + + // make sure we have balanced parenthesis + $token_count = array_count_values($tokens); + if(isset($token_count['(']) && $token_count['('] != $token_count[')']) { + $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__); + } + + $is_arg_stack = array(); + + for ($i = 0; $i < count($tokens); $i++) { + + $token = &$tokens[$i]; + + switch (strtolower($token)) { + case '!': + case '%': + case '!==': + case '==': + case '===': + case '>': + case '<': + case '!=': + case '<>': + case '<<': + case '>>': + case '<=': + case '>=': + case '&&': + case '||': + case '|': + case '^': + case '&': + case '~': + case ')': + case ',': + case '+': + case '-': + case '*': + case '/': + case '@': + break; + + case 'eq': + $token = '=='; + break; + + case 'ne': + case 'neq': + $token = '!='; + break; + + case 'lt': + $token = '<'; + break; + + case 'le': + case 'lte': + $token = '<='; + break; + + case 'gt': + $token = '>'; + break; + + case 'ge': + case 'gte': + $token = '>='; + break; + + case 'and': + $token = '&&'; + break; + + case 'or': + $token = '||'; + break; + + case 'not': + $token = '!'; + break; + + case 'mod': + $token = '%'; + break; + + case '(': + array_push($is_arg_stack, $i); + break; + + case 'is': + /* If last token was a ')', we operate on the parenthesized + expression. The start of the expression is on the stack. + Otherwise, we operate on the last encountered token. */ + if ($tokens[$i-1] == ')') + $is_arg_start = array_pop($is_arg_stack); + else + $is_arg_start = $i-1; + /* Construct the argument for 'is' expression, so it knows + what to operate on. */ + $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start)); + + /* Pass all tokens from next one until the end to the + 'is' expression parsing function. The function will + return modified tokens, where the first one is the result + of the 'is' expression and the rest are the tokens it + didn't touch. */ + $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1)); + + /* Replace the old tokens with the new ones. */ + array_splice($tokens, $is_arg_start, count($tokens), $new_tokens); + + /* Adjust argument start so that it won't change from the + current position for the next iteration. */ + $i = $is_arg_start; + break; + + default: + if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) { + // function call + if($this->security && + !in_array($token, $this->security_settings['IF_FUNCS'])) { + $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); + } + } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') { + // variable function call + $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__); + } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) { + // object or variable + $token = $this->_parse_var_props($token); + } elseif(is_numeric($token)) { + // number, skip it + } else { + $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__); + } + break; + } + } + + if ($elseif) + return ''; + else + return ''; + } + + + function _compile_arg_list($type, $name, $attrs, &$cache_code) { + $arg_list = array(); + + if (isset($type) && isset($name) + && isset($this->_plugins[$type]) + && isset($this->_plugins[$type][$name]) + && empty($this->_plugins[$type][$name][4]) + && is_array($this->_plugins[$type][$name][5]) + ) { + /* we have a list of parameters that should be cached */ + $_cache_attrs = $this->_plugins[$type][$name][5]; + $_count = $this->_cache_attrs_count++; + $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');"; + + } else { + /* no parameters are cached */ + $_cache_attrs = null; + } + + foreach ($attrs as $arg_name => $arg_value) { + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; + if (is_null($arg_value)) + $arg_value = 'null'; + if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) { + $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)"; + } else { + $arg_list[] = "'$arg_name' => $arg_value"; + } + } + return $arg_list; + } + + /** + * Parse is expression + * + * @param string $is_arg + * @param array $tokens + * @return array + */ + function _parse_is_expr($is_arg, $tokens) + { + $expr_end = 0; + $negate_expr = false; + + if (($first_token = array_shift($tokens)) == 'not') { + $negate_expr = true; + $expr_type = array_shift($tokens); + } else + $expr_type = $first_token; + + switch ($expr_type) { + case 'even': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))"; + } else + $expr = "!(1 & $is_arg)"; + break; + + case 'odd': + if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))"; + } else + $expr = "(1 & $is_arg)"; + break; + + case 'div': + if (@$tokens[$expr_end] == 'by') { + $expr_end++; + $expr_arg = $tokens[$expr_end++]; + $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")"; + } else { + $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__); + } + break; + + default: + $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__); + break; + } + + if ($negate_expr) { + $expr = "!($expr)"; + } + + array_splice($tokens, 0, $expr_end, $expr); + + return $tokens; + } + + + /** + * Parse attribute string + * + * @param string $tag_args + * @return array + */ + function _parse_attrs($tag_args) + { + + /* Tokenize tag attributes. */ + preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+) + )+ | + [=] + ~x', $tag_args, $match); + $tokens = $match[0]; + + $attrs = array(); + /* Parse state: + 0 - expecting attribute name + 1 - expecting '=' + 2 - expecting attribute value (not '=') */ + $state = 0; + + foreach ($tokens as $token) { + switch ($state) { + case 0: + /* If the token is a valid identifier, we set attribute name + and go to state 1. */ + if (preg_match('~^\w+$~', $token)) { + $attr_name = $token; + $state = 1; + } else + $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__); + break; + + case 1: + /* If the token is '=', then we go to state 2. */ + if ($token == '=') { + $state = 2; + } else + $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); + break; + + case 2: + /* If token is not '=', we set the attribute value and go to + state 0. */ + if ($token != '=') { + /* We booleanize the token if it's a non-quoted possible + boolean value. */ + if (preg_match('~^(on|yes|true)$~', $token)) { + $token = 'true'; + } else if (preg_match('~^(off|no|false)$~', $token)) { + $token = 'false'; + } else if ($token == 'null') { + $token = 'null'; + } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) { + /* treat integer literally */ + } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) { + /* treat as a string, double-quote it escaping quotes */ + $token = '"'.addslashes($token).'"'; + } + + $attrs[$attr_name] = $token; + $state = 0; + } else + $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__); + break; + } + $last_token = $token; + } + + if($state != 0) { + if($state == 1) { + $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__); + } else { + $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__); + } + } + + $this->_parse_vars_props($attrs); + + return $attrs; + } + + /** + * compile multiple variables and section properties tokens into + * PHP code + * + * @param array $tokens + */ + function _parse_vars_props(&$tokens) + { + foreach($tokens as $key => $val) { + $tokens[$key] = $this->_parse_var_props($val); + } + } + + /** + * compile single variable and section properties token into + * PHP code + * + * @param string $val + * @param string $tag_attrs + * @return string + */ + function _parse_var_props($val) + { + $val = trim($val); + + if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) { + // $ variable or object + $return = $this->_parse_var($match[1]); + $modifiers = $match[2]; + if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) { + $_default_mod_string = implode('|',(array)$this->default_modifiers); + $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers; + } + $this->_parse_modifiers($return, $modifiers); + return $return; + } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { + // double quoted text + preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); + $return = $this->_expand_quoted_text($match[1]); + if($match[2] != '') { + $this->_parse_modifiers($return, $match[2]); + } + return $return; + } + elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { + // numerical constant + preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); + if($match[2] != '') { + $this->_parse_modifiers($match[1], $match[2]); + return $match[1]; + } + } + elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { + // single quoted text + preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match); + if($match[2] != '') { + $this->_parse_modifiers($match[1], $match[2]); + return $match[1]; + } + } + elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { + // config var + return $this->_parse_conf_var($val); + } + elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) { + // section var + return $this->_parse_section_prop($val); + } + elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) { + // literal string + return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"'); + } + return $val; + } + + /** + * expand quoted text with embedded variables + * + * @param string $var_expr + * @return string + */ + function _expand_quoted_text($var_expr) + { + // if contains unescaped $, expand it + if(preg_match_all('~(?:\`(?_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?_parse_var(str_replace('`','',$_var)) . ')."', $var_expr); + } + $_return = preg_replace('~\.""|(?_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE); + + if(count($_math_vars) > 1) { + $_first_var = ""; + $_complete_var = ""; + $_output = ""; + // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter) + foreach($_math_vars as $_k => $_math_var) { + $_math_var = $_math_vars[$_k]; + + if(!empty($_math_var) || is_numeric($_math_var)) { + // hit a math operator, so process the stuff which came before it + if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) { + $_has_math = true; + if(!empty($_complete_var) || is_numeric($_complete_var)) { + $_output .= $this->_parse_var($_complete_var); + } + + // just output the math operator to php + $_output .= $_math_var; + + if(empty($_first_var)) + $_first_var = $_complete_var; + + $_complete_var = ""; + } else { + $_complete_var .= $_math_var; + } + } + } + if($_has_math) { + if(!empty($_complete_var) || is_numeric($_complete_var)) + $_output .= $this->_parse_var($_complete_var); + + // get the modifiers working (only the last var from math + modifier is left) + $var_expr = $_complete_var; + } + } + + // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit) + if(is_numeric(substr($var_expr, 0, 1))) + $_var_ref = $var_expr; + else + $_var_ref = substr($var_expr, 1); + + if(!$_has_math) { + + // get [foo] and .foo and ->foo and (...) pieces + preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match); + + $_indexes = $match[0]; + $_var_name = array_shift($_indexes); + + /* Handle $smarty.* variable references as a special case. */ + if ($_var_name == 'smarty') { + /* + * If the reference could be compiled, use the compiled output; + * otherwise, fall back on the $smarty variable generated at + * run-time. + */ + if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) { + $_output = $smarty_ref; + } else { + $_var_name = substr(array_shift($_indexes), 1); + $_output = "\$this->_smarty_vars['$_var_name']"; + } + } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) { + // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers + if(count($_indexes) > 0) + { + $_var_name .= implode("", $_indexes); + $_indexes = array(); + } + $_output = $_var_name; + } else { + $_output = "\$this->_tpl_vars['$_var_name']"; + } + + foreach ($_indexes as $_index) { + if (substr($_index, 0, 1) == '[') { + $_index = substr($_index, 1, -1); + if (is_numeric($_index)) { + $_output .= "[$_index]"; + } elseif (substr($_index, 0, 1) == '$') { + if (strpos($_index, '.') !== false) { + $_output .= '[' . $this->_parse_var($_index) . ']'; + } else { + $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]"; + } + } else { + $_var_parts = explode('.', $_index); + $_var_section = $_var_parts[0]; + $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index'; + $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]"; + } + } else if (substr($_index, 0, 1) == '.') { + if (substr($_index, 1, 1) == '$') + $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]"; + else + $_output .= "['" . substr($_index, 1) . "']"; + } else if (substr($_index,0,2) == '->') { + if(substr($_index,2,2) == '__') { + $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__); + } elseif($this->security && substr($_index, 2, 1) == '_') { + $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); + } elseif (substr($_index, 2, 1) == '$') { + if ($this->security) { + $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__); + } else { + $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}'; + } + } else { + $_output .= $_index; + } + } elseif (substr($_index, 0, 1) == '(') { + $_index = $this->_parse_parenth_args($_index); + $_output .= $_index; + } else { + $_output .= $_index; + } + } + } + + return $_output; + } + + /** + * parse arguments in function call parenthesis + * + * @param string $parenth_args + * @return string + */ + function _parse_parenth_args($parenth_args) + { + preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match); + $orig_vals = $match = $match[0]; + $this->_parse_vars_props($match); + $replace = array(); + for ($i = 0, $count = count($match); $i < $count; $i++) { + $replace[$orig_vals[$i]] = $match[$i]; + } + return strtr($parenth_args, $replace); + } + + /** + * parse configuration variable expression into PHP code + * + * @param string $conf_var_expr + */ + function _parse_conf_var($conf_var_expr) + { + $parts = explode('|', $conf_var_expr, 2); + $var_ref = $parts[0]; + $modifiers = isset($parts[1]) ? $parts[1] : ''; + + $var_name = substr($var_ref, 1, -1); + + $output = "\$this->_config[0]['vars']['$var_name']"; + + $this->_parse_modifiers($output, $modifiers); + + return $output; + } + + /** + * parse section property expression into PHP code + * + * @param string $section_prop_expr + * @return string + */ + function _parse_section_prop($section_prop_expr) + { + $parts = explode('|', $section_prop_expr, 2); + $var_ref = $parts[0]; + $modifiers = isset($parts[1]) ? $parts[1] : ''; + + preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match); + $section_name = $match[1]; + $prop_name = $match[2]; + + $output = "\$this->_sections['$section_name']['$prop_name']"; + + $this->_parse_modifiers($output, $modifiers); + + return $output; + } + + + /** + * parse modifier chain into PHP code + * + * sets $output to parsed modified chain + * @param string $output + * @param string $modifier_string + */ + function _parse_modifiers(&$output, $modifier_string) + { + preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match); + list(, $_modifiers, $modifier_arg_strings) = $_match; + + for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) { + $_modifier_name = $_modifiers[$_i]; + + if($_modifier_name == 'smarty') { + // skip smarty modifier + continue; + } + + preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match); + $_modifier_args = $_match[1]; + + if (substr($_modifier_name, 0, 1) == '@') { + $_map_array = false; + $_modifier_name = substr($_modifier_name, 1); + } else { + $_map_array = true; + } + + if (empty($this->_plugins['modifier'][$_modifier_name]) + && !$this->_get_plugin_filepath('modifier', $_modifier_name) + && function_exists($_modifier_name)) { + if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) { + $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__); + } else { + $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false); + } + } + $this->_add_plugin('modifier', $_modifier_name); + + $this->_parse_vars_props($_modifier_args); + + if($_modifier_name == 'default') { + // supress notifications of default modifier vars and args + if(substr($output, 0, 1) == '$') { + $output = '@' . $output; + } + if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') { + $_modifier_args[0] = '@' . $_modifier_args[0]; + } + } + if (count($_modifier_args) > 0) + $_modifier_args = ', '.implode(', ', $_modifier_args); + else + $_modifier_args = ''; + + if ($_map_array) { + $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))"; + + } else { + + $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)"; + + } + } + } + + + /** + * add plugin + * + * @param string $type + * @param string $name + * @param boolean? $delayed_loading + */ + function _add_plugin($type, $name, $delayed_loading = null) + { + if (!isset($this->_plugin_info[$type])) { + $this->_plugin_info[$type] = array(); + } + if (!isset($this->_plugin_info[$type][$name])) { + $this->_plugin_info[$type][$name] = array($this->_current_file, + $this->_current_line_no, + $delayed_loading); + } + } + + + /** + * Compiles references of type $smarty.foo + * + * @param string $indexes + * @return string + */ + function _compile_smarty_ref(&$indexes) + { + /* Extract the reference name. */ + $_ref = substr($indexes[0], 1); + foreach($indexes as $_index_no=>$_index) { + if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) { + $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__); + } + } + + switch ($_ref) { + case 'now': + $compiled_ref = 'time()'; + $_max_index = 1; + break; + + case 'foreach': + array_shift($indexes); + $_var = $this->_parse_var_props(substr($indexes[0], 1)); + $_propname = substr($indexes[1], 1); + $_max_index = 1; + switch ($_propname) { + case 'index': + array_shift($indexes); + $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)"; + break; + + case 'first': + array_shift($indexes); + $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)"; + break; + + case 'last': + array_shift($indexes); + $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])"; + break; + + case 'show': + array_shift($indexes); + $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)"; + break; + + default: + unset($_max_index); + $compiled_ref = "\$this->_foreach[$_var]"; + } + break; + + case 'section': + array_shift($indexes); + $_var = $this->_parse_var_props(substr($indexes[0], 1)); + $compiled_ref = "\$this->_sections[$_var]"; + break; + + case 'get': + $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']"; + break; + + case 'post': + $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']"; + break; + + case 'cookies': + $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']"; + break; + + case 'env': + $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']"; + break; + + case 'server': + $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']"; + break; + + case 'session': + $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']"; + break; + + /* + * These cases are handled either at run-time or elsewhere in the + * compiler. + */ + case 'request': + if ($this->request_use_auto_globals) { + $compiled_ref = '$_REQUEST'; + break; + } else { + $this->_init_smarty_vars = true; + } + return null; + + case 'capture': + return null; + + case 'template': + $compiled_ref = "'$this->_current_file'"; + $_max_index = 1; + break; + + case 'version': + $compiled_ref = "'$this->_version'"; + $_max_index = 1; + break; + + case 'const': + if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) { + $this->_syntax_error("(secure mode) constants not permitted", + E_USER_WARNING, __FILE__, __LINE__); + return; + } + array_shift($indexes); + if (preg_match('!^\.\w+$!', $indexes[0])) { + $compiled_ref = '@' . substr($indexes[0], 1); + } else { + $_val = $this->_parse_var_props(substr($indexes[0], 1)); + $compiled_ref = '@constant(' . $_val . ')'; + } + $_max_index = 1; + break; + + case 'config': + $compiled_ref = "\$this->_config[0]['vars']"; + $_max_index = 3; + break; + + case 'ldelim': + $compiled_ref = "'$this->left_delimiter'"; + break; + + case 'rdelim': + $compiled_ref = "'$this->right_delimiter'"; + break; + + default: + $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__); + break; + } + + if (isset($_max_index) && count($indexes) > $_max_index) { + $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__); + } + + array_shift($indexes); + return $compiled_ref; + } + + /** + * compiles call to plugin of type $type with name $name + * returns a string containing the function-name or method call + * without the paramter-list that would have follow to make the + * call valid php-syntax + * + * @param string $type + * @param string $name + * @return string + */ + function _compile_plugin_call($type, $name) { + if (isset($this->_plugins[$type][$name])) { + /* plugin loaded */ + if (is_array($this->_plugins[$type][$name][0])) { + return ((is_object($this->_plugins[$type][$name][0][0])) ? + "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */ + : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */ + ). $this->_plugins[$type][$name][0][1]; + + } else { + /* function callback */ + return $this->_plugins[$type][$name][0]; + + } + } else { + /* plugin not loaded -> auto-loadable-plugin */ + return 'smarty_'.$type.'_'.$name; + + } + } + + /** + * load pre- and post-filters + */ + function _load_filters() + { + if (count($this->_plugins['prefilter']) > 0) { + foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) { + if ($prefilter === false) { + unset($this->_plugins['prefilter'][$filter_name]); + $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false))); + require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); + smarty_core_load_plugins($_params, $this); + } + } + } + if (count($this->_plugins['postfilter']) > 0) { + foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) { + if ($postfilter === false) { + unset($this->_plugins['postfilter'][$filter_name]); + $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false))); + require_once(SMARTY_CORE_DIR . 'core.load_plugins.php'); + smarty_core_load_plugins($_params, $this); + } + } + } + } + + + /** + * Quote subpattern references + * + * @param string $string + * @return string + */ + function _quote_replace($string) + { + return strtr($string, array('\\' => '\\\\', '$' => '\\$')); + } + + /** + * display Smarty syntax error + * + * @param string $error_msg + * @param integer $error_type + * @param string $file + * @param integer $line + */ + function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null) + { + $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type); + } + + + /** + * check if the compilation changes from cacheable to + * non-cacheable state with the beginning of the current + * plugin. return php-code to reflect the transition. + * @return string + */ + function _push_cacheable_state($type, $name) { + $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4]; + if ($_cacheable + || 0<$this->_cacheable_state++) return ''; + if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty')); + $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:' + . $this->_cache_serial . '#' . $this->_nocache_count + . '}\'; };'; + return $_ret; + } + + + /** + * check if the compilation changes from non-cacheable to + * cacheable state with the end of the current plugin return + * php-code to reflect the transition. + * @return string + */ + function _pop_cacheable_state($type, $name) { + $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4]; + if ($_cacheable + || --$this->_cacheable_state>0) return ''; + return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:' + . $this->_cache_serial . '#' . ($this->_nocache_count++) + . '}\'; };'; + } + + + /** + * push opening tag-name, file-name and line-number on the tag-stack + * @param string the opening tag's name + */ + function _push_tag($open_tag) + { + array_push($this->_tag_stack, array($open_tag, $this->_current_line_no)); + } + + /** + * pop closing tag-name + * raise an error if this stack-top doesn't match with the closing tag + * @param string the closing tag's name + * @return string the opening tag's name + */ + function _pop_tag($close_tag) + { + $message = ''; + if (count($this->_tag_stack)>0) { + list($_open_tag, $_line_no) = array_pop($this->_tag_stack); + if ($close_tag == $_open_tag) { + return $_open_tag; + } + if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) { + return $this->_pop_tag($close_tag); + } + if ($close_tag == 'section' && $_open_tag == 'sectionelse') { + $this->_pop_tag($close_tag); + return $_open_tag; + } + if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') { + $this->_pop_tag($close_tag); + return $_open_tag; + } + if ($_open_tag == 'else' || $_open_tag == 'elseif') { + $_open_tag = 'if'; + } elseif ($_open_tag == 'sectionelse') { + $_open_tag = 'section'; + } elseif ($_open_tag == 'foreachelse') { + $_open_tag = 'foreach'; + } + $message = " expected {/$_open_tag} (opened line $_line_no)."; + } + $this->_syntax_error("mismatched tag {/$close_tag}.$message", + E_USER_ERROR, __FILE__, __LINE__); + } + +} + +/** + * compare to values by their string length + * + * @access private + * @param string $a + * @param string $b + * @return 0|-1|1 + */ +function _smarty_sort_length($a, $b) +{ + if($a == $b) + return 0; + + if(strlen($a) == strlen($b)) + return ($a > $b) ? -1 : 1; + + return (strlen($a) > strlen($b)) ? -1 : 1; +} + + +/* vim: set et: */ + +?> diff --git a/library/smarty/debug.tpl b/library/smarty/debug.tpl new file mode 100755 index 0000000..38c6e73 --- /dev/null +++ b/library/smarty/debug.tpl @@ -0,0 +1,64 @@ +{* Smarty *} + +{* debug.tpl, last updated version 2.0.1 *} + +{assign_debug_info} + +{if isset($_smarty_debug_output) and $_smarty_debug_output eq "html"} + + + + {section name=templates loop=$_debug_tpls} + + {sectionelse} + + {/section} + + {section name=vars loop=$_debug_keys} + + {sectionelse} + + {/section} + + {section name=config_vars loop=$_debug_config_keys} + + {sectionelse} + + {/section} +
    Smarty Debug Console
    included templates & config files (load time in seconds):
    {section name=indent loop=$_debug_tpls[templates].depth}   {/section}{$_debug_tpls[templates].filename|escape:html}{if isset($_debug_tpls[templates].exec_time)} ({$_debug_tpls[templates].exec_time|string_format:"%.5f"}){if %templates.index% eq 0} (total){/if}{/if}
    no templates included
    assigned template variables:
    {ldelim}${$_debug_keys[vars]}{rdelim}{$_debug_vals[vars]|@debug_print_var}
    no template variables assigned
    assigned config file variables (outer template scope):
    {ldelim}#{$_debug_config_keys[config_vars]}#{rdelim}{$_debug_config_vals[config_vars]|@debug_print_var}
    no config vars assigned
    + +{else} + +{/if} diff --git a/library/smarty/internals/core.assemble_plugin_filepath.php b/library/smarty/internals/core.assemble_plugin_filepath.php new file mode 100755 index 0000000..fc64b8c --- /dev/null +++ b/library/smarty/internals/core.assemble_plugin_filepath.php @@ -0,0 +1,67 @@ +plugins_dir as $_plugin_dir) { + + $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename; + + // see if path is relative + if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/", $_plugin_dir)) { + $_relative_paths[] = $_plugin_dir; + // relative path, see if it is in the SMARTY_DIR + if (@is_readable(SMARTY_DIR . $_plugin_filepath)) { + $_return = SMARTY_DIR . $_plugin_filepath; + break; + } + } + // try relative to cwd (or absolute) + if (@is_readable($_plugin_filepath)) { + $_return = $_plugin_filepath; + break; + } + } + + if($_return === false) { + // still not found, try PHP include_path + if(isset($_relative_paths)) { + foreach ((array)$_relative_paths as $_plugin_dir) { + + $_plugin_filepath = $_plugin_dir . DIRECTORY_SEPARATOR . $_plugin_filename; + + $_params = array('file_path' => $_plugin_filepath); + require_once(SMARTY_CORE_DIR . 'core.get_include_path.php'); + if(smarty_core_get_include_path($_params, $smarty)) { + $_return = $_params['new_file_path']; + break; + } + } + } + } + $_filepaths_cache[$_plugin_filename] = $_return; + return $_return; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.assign_smarty_interface.php b/library/smarty/internals/core.assign_smarty_interface.php new file mode 100755 index 0000000..500ba9a --- /dev/null +++ b/library/smarty/internals/core.assign_smarty_interface.php @@ -0,0 +1,43 @@ + + * Name: assign_smarty_interface
    + * Purpose: assign the $smarty interface variable + * @param array Format: null + * @param Smarty + */ +function smarty_core_assign_smarty_interface($params, &$smarty) +{ + if (isset($smarty->_smarty_vars) && isset($smarty->_smarty_vars['request'])) { + return; + } + + $_globals_map = array('g' => 'HTTP_GET_VARS', + 'p' => 'HTTP_POST_VARS', + 'c' => 'HTTP_COOKIE_VARS', + 's' => 'HTTP_SERVER_VARS', + 'e' => 'HTTP_ENV_VARS'); + + $_smarty_vars_request = array(); + + foreach (preg_split('!!', strtolower($smarty->request_vars_order)) as $_c) { + if (isset($_globals_map[$_c])) { + $_smarty_vars_request = array_merge($_smarty_vars_request, $GLOBALS[$_globals_map[$_c]]); + } + } + $_smarty_vars_request = @array_merge($_smarty_vars_request, $GLOBALS['HTTP_SESSION_VARS']); + + $smarty->_smarty_vars['request'] = $_smarty_vars_request; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.create_dir_structure.php b/library/smarty/internals/core.create_dir_structure.php new file mode 100755 index 0000000..abc2850 --- /dev/null +++ b/library/smarty/internals/core.create_dir_structure.php @@ -0,0 +1,79 @@ +_dir_perms) && !is_dir($_new_dir)) { + $smarty->trigger_error("problem creating directory '" . $_new_dir . "'"); + return false; + } + $_new_dir .= '/'; + } + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.display_debug_console.php b/library/smarty/internals/core.display_debug_console.php new file mode 100755 index 0000000..5968676 --- /dev/null +++ b/library/smarty/internals/core.display_debug_console.php @@ -0,0 +1,61 @@ + + * Name: display_debug_console
    + * Purpose: display the javascript debug console window + * @param array Format: null + * @param Smarty + */ +function smarty_core_display_debug_console($params, &$smarty) +{ + // we must force compile the debug template in case the environment + // changed between separate applications. + + if(empty($smarty->debug_tpl)) { + // set path to debug template from SMARTY_DIR + $smarty->debug_tpl = SMARTY_DIR . 'debug.tpl'; + if($smarty->security && is_file($smarty->debug_tpl)) { + $smarty->secure_dir[] = realpath($smarty->debug_tpl); + } + $smarty->debug_tpl = 'file:' . SMARTY_DIR . 'debug.tpl'; + } + + $_ldelim_orig = $smarty->left_delimiter; + $_rdelim_orig = $smarty->right_delimiter; + + $smarty->left_delimiter = '{'; + $smarty->right_delimiter = '}'; + + $_compile_id_orig = $smarty->_compile_id; + $smarty->_compile_id = null; + + $_compile_path = $smarty->_get_compile_path($smarty->debug_tpl); + if ($smarty->_compile_resource($smarty->debug_tpl, $_compile_path)) + { + ob_start(); + $smarty->_include($_compile_path); + $_results = ob_get_contents(); + ob_end_clean(); + } else { + $_results = ''; + } + + $smarty->_compile_id = $_compile_id_orig; + + $smarty->left_delimiter = $_ldelim_orig; + $smarty->right_delimiter = $_rdelim_orig; + + return $_results; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.get_include_path.php b/library/smarty/internals/core.get_include_path.php new file mode 100755 index 0000000..0cfdbdc --- /dev/null +++ b/library/smarty/internals/core.get_include_path.php @@ -0,0 +1,44 @@ + diff --git a/library/smarty/internals/core.get_microtime.php b/library/smarty/internals/core.get_microtime.php new file mode 100755 index 0000000..3c998a7 --- /dev/null +++ b/library/smarty/internals/core.get_microtime.php @@ -0,0 +1,23 @@ + diff --git a/library/smarty/internals/core.get_php_resource.php b/library/smarty/internals/core.get_php_resource.php new file mode 100755 index 0000000..8fa1da6 --- /dev/null +++ b/library/smarty/internals/core.get_php_resource.php @@ -0,0 +1,80 @@ +trusted_dir; + $smarty->_parse_resource_name($params, $smarty); + + /* + * Find out if the resource exists. + */ + + if ($params['resource_type'] == 'file') { + $_readable = false; + if(file_exists($params['resource_name']) && is_readable($params['resource_name'])) { + $_readable = true; + } else { + // test for file in include_path + $_params = array('file_path' => $params['resource_name']); + require_once(SMARTY_CORE_DIR . 'core.get_include_path.php'); + if(smarty_core_get_include_path($_params, $smarty)) { + $_include_path = $_params['new_file_path']; + $_readable = true; + } + } + } else if ($params['resource_type'] != 'file') { + $_template_source = null; + $_readable = is_callable($smarty->_plugins['resource'][$params['resource_type']][0][0]) + && call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][0], + array($params['resource_name'], &$_template_source, &$smarty)); + } + + /* + * Set the error function, depending on which class calls us. + */ + if (method_exists($smarty, '_syntax_error')) { + $_error_funcc = '_syntax_error'; + } else { + $_error_funcc = 'trigger_error'; + } + + if ($_readable) { + if ($smarty->security) { + require_once(SMARTY_CORE_DIR . 'core.is_trusted.php'); + if (!smarty_core_is_trusted($params, $smarty)) { + $smarty->$_error_funcc('(secure mode) ' . $params['resource_type'] . ':' . $params['resource_name'] . ' is not trusted'); + return false; + } + } + } else { + $smarty->$_error_funcc($params['resource_type'] . ':' . $params['resource_name'] . ' is not readable'); + return false; + } + + if ($params['resource_type'] == 'file') { + $params['php_resource'] = $params['resource_name']; + } else { + $params['php_resource'] = $_template_source; + } + return true; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.is_secure.php b/library/smarty/internals/core.is_secure.php new file mode 100755 index 0000000..15c729e --- /dev/null +++ b/library/smarty/internals/core.is_secure.php @@ -0,0 +1,59 @@ +security || $smarty->security_settings['INCLUDE_ANY']) { + return true; + } + + if ($params['resource_type'] == 'file') { + $_rp = realpath($params['resource_name']); + if (isset($params['resource_base_path'])) { + foreach ((array)$params['resource_base_path'] as $curr_dir) { + if ( ($_cd = realpath($curr_dir)) !== false && + strncmp($_rp, $_cd, strlen($_cd)) == 0 && + substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) { + return true; + } + } + } + if (!empty($smarty->secure_dir)) { + foreach ((array)$smarty->secure_dir as $curr_dir) { + if ( ($_cd = realpath($curr_dir)) !== false) { + if($_cd == $_rp) { + return true; + } elseif (strncmp($_rp, $_cd, strlen($_cd)) == 0 && + substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR) { + return true; + } + } + } + } + } else { + // resource is not on local file system + return call_user_func_array( + $smarty->_plugins['resource'][$params['resource_type']][0][2], + array($params['resource_name'], &$smarty)); + } + + return false; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.is_trusted.php b/library/smarty/internals/core.is_trusted.php new file mode 100755 index 0000000..ecf703c --- /dev/null +++ b/library/smarty/internals/core.is_trusted.php @@ -0,0 +1,47 @@ +trusted_dir)) { + $_rp = realpath($params['resource_name']); + foreach ((array)$smarty->trusted_dir as $curr_dir) { + if (!empty($curr_dir) && is_readable ($curr_dir)) { + $_cd = realpath($curr_dir); + if (strncmp($_rp, $_cd, strlen($_cd)) == 0 + && substr($_rp, strlen($_cd), 1) == DIRECTORY_SEPARATOR ) { + $_smarty_trusted = true; + break; + } + } + } + } + + } else { + // resource is not on local file system + $_smarty_trusted = call_user_func_array($smarty->_plugins['resource'][$params['resource_type']][0][3], + array($params['resource_name'], $smarty)); + } + + return $_smarty_trusted; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.load_plugins.php b/library/smarty/internals/core.load_plugins.php new file mode 100755 index 0000000..6f412ec --- /dev/null +++ b/library/smarty/internals/core.load_plugins.php @@ -0,0 +1,125 @@ +_plugins[$_type][$_name]; + + /* + * We do not load plugin more than once for each instance of Smarty. + * The following code checks for that. The plugin can also be + * registered dynamically at runtime, in which case template file + * and line number will be unknown, so we fill them in. + * + * The final element of the info array is a flag that indicates + * whether the dynamically registered plugin function has been + * checked for existence yet or not. + */ + if (isset($_plugin)) { + if (empty($_plugin[3])) { + if (!is_callable($_plugin[0])) { + $smarty->_trigger_fatal_error("[plugin] $_type '$_name' is not implemented", $_tpl_file, $_tpl_line, __FILE__, __LINE__); + } else { + $_plugin[1] = $_tpl_file; + $_plugin[2] = $_tpl_line; + $_plugin[3] = true; + if (!isset($_plugin[4])) $_plugin[4] = true; /* cacheable */ + } + } + continue; + } else if ($_type == 'insert') { + /* + * For backwards compatibility, we check for insert functions in + * the symbol table before trying to load them as a plugin. + */ + $_plugin_func = 'insert_' . $_name; + if (function_exists($_plugin_func)) { + $_plugin = array($_plugin_func, $_tpl_file, $_tpl_line, true, false); + continue; + } + } + + $_plugin_file = $smarty->_get_plugin_filepath($_type, $_name); + + if (! $_found = ($_plugin_file != false)) { + $_message = "could not load plugin file '$_type.$_name.php'\n"; + } + + /* + * If plugin file is found, it -must- provide the properly named + * plugin function. In case it doesn't, simply output the error and + * do not fall back on any other method. + */ + if ($_found) { + include_once $_plugin_file; + + $_plugin_func = 'smarty_' . $_type . '_' . $_name; + if (!function_exists($_plugin_func)) { + $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", $_tpl_file, $_tpl_line, __FILE__, __LINE__); + continue; + } + } + /* + * In case of insert plugins, their code may be loaded later via + * 'script' attribute. + */ + else if ($_type == 'insert' && $_delayed_loading) { + $_plugin_func = 'smarty_' . $_type . '_' . $_name; + $_found = true; + } + + /* + * Plugin specific processing and error checking. + */ + if (!$_found) { + if ($_type == 'modifier') { + /* + * In case modifier falls back on using PHP functions + * directly, we only allow those specified in the security + * context. + */ + if ($smarty->security && !in_array($_name, $smarty->security_settings['MODIFIER_FUNCS'])) { + $_message = "(secure mode) modifier '$_name' is not allowed"; + } else { + if (!function_exists($_name)) { + $_message = "modifier '$_name' is not implemented"; + } else { + $_plugin_func = $_name; + $_found = true; + } + } + } else if ($_type == 'function') { + /* + * This is a catch-all situation. + */ + $_message = "unknown tag - '$_name'"; + } + } + + if ($_found) { + $smarty->_plugins[$_type][$_name] = array($_plugin_func, $_tpl_file, $_tpl_line, true, true); + } else { + // output error + $smarty->_trigger_fatal_error('[plugin] ' . $_message, $_tpl_file, $_tpl_line, __FILE__, __LINE__); + } + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.load_resource_plugin.php b/library/smarty/internals/core.load_resource_plugin.php new file mode 100755 index 0000000..8a084f1 --- /dev/null +++ b/library/smarty/internals/core.load_resource_plugin.php @@ -0,0 +1,74 @@ +_plugins['resource'][$params['type']]; + if (isset($_plugin)) { + if (!$_plugin[1] && count($_plugin[0])) { + $_plugin[1] = true; + foreach ($_plugin[0] as $_plugin_func) { + if (!is_callable($_plugin_func)) { + $_plugin[1] = false; + break; + } + } + } + + if (!$_plugin[1]) { + $smarty->_trigger_fatal_error("[plugin] resource '" . $params['type'] . "' is not implemented", null, null, __FILE__, __LINE__); + } + + return; + } + + $_plugin_file = $smarty->_get_plugin_filepath('resource', $params['type']); + $_found = ($_plugin_file != false); + + if ($_found) { /* + * If the plugin file is found, it -must- provide the properly named + * plugin functions. + */ + include_once($_plugin_file); + + /* + * Locate functions that we require the plugin to provide. + */ + $_resource_ops = array('source', 'timestamp', 'secure', 'trusted'); + $_resource_funcs = array(); + foreach ($_resource_ops as $_op) { + $_plugin_func = 'smarty_resource_' . $params['type'] . '_' . $_op; + if (!function_exists($_plugin_func)) { + $smarty->_trigger_fatal_error("[plugin] function $_plugin_func() not found in $_plugin_file", null, null, __FILE__, __LINE__); + return; + } else { + $_resource_funcs[] = $_plugin_func; + } + } + + $smarty->_plugins['resource'][$params['type']] = array($_resource_funcs, true); + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.process_cached_inserts.php b/library/smarty/internals/core.process_cached_inserts.php new file mode 100755 index 0000000..046e2c4 --- /dev/null +++ b/library/smarty/internals/core.process_cached_inserts.php @@ -0,0 +1,71 @@ +_smarty_md5.'{insert_cache (.*)}'.$smarty->_smarty_md5.'!Uis', + $params['results'], $match); + list($cached_inserts, $insert_args) = $match; + + for ($i = 0, $for_max = count($cached_inserts); $i < $for_max; $i++) { + if ($smarty->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $debug_start_time = smarty_core_get_microtime($_params, $smarty); + } + + $args = unserialize($insert_args[$i]); + $name = $args['name']; + + if (isset($args['script'])) { + $_params = array('resource_name' => $smarty->_dequote($args['script'])); + require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); + if(!smarty_core_get_php_resource($_params, $smarty)) { + return false; + } + $resource_type = $_params['resource_type']; + $php_resource = $_params['php_resource']; + + + if ($resource_type == 'file') { + $smarty->_include($php_resource, true); + } else { + $smarty->_eval($php_resource); + } + } + + $function_name = $smarty->_plugins['insert'][$name][0]; + if (empty($args['assign'])) { + $replace = $function_name($args, $smarty); + } else { + $smarty->assign($args['assign'], $function_name($args, $smarty)); + $replace = ''; + } + + $params['results'] = substr_replace($params['results'], $replace, strpos($params['results'], $cached_inserts[$i]), strlen($cached_inserts[$i])); + if ($smarty->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $smarty->_smarty_debug_info[] = array('type' => 'insert', + 'filename' => 'insert_'.$name, + 'depth' => $smarty->_inclusion_depth, + 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $debug_start_time); + } + } + + return $params['results']; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.process_compiled_include.php b/library/smarty/internals/core.process_compiled_include.php new file mode 100755 index 0000000..76492eb --- /dev/null +++ b/library/smarty/internals/core.process_compiled_include.php @@ -0,0 +1,37 @@ +_cache_including; + $smarty->_cache_including = true; + + $_return = $params['results']; + + foreach ($smarty->_cache_info['cache_serials'] as $_include_file_path=>$_cache_serial) { + $smarty->_include($_include_file_path, true); + } + + foreach ($smarty->_cache_serials as $_include_file_path=>$_cache_serial) { + $_return = preg_replace_callback('!(\{nocache\:('.$_cache_serial.')#(\d+)\})!s', + array(&$smarty, '_process_compiled_include_callback'), + $_return); + } + $smarty->_cache_including = $_cache_including; + return $_return; +} + +?> diff --git a/library/smarty/internals/core.read_cache_file.php b/library/smarty/internals/core.read_cache_file.php new file mode 100755 index 0000000..2205b2a --- /dev/null +++ b/library/smarty/internals/core.read_cache_file.php @@ -0,0 +1,101 @@ +force_compile) { + // force compile enabled, always regenerate + return false; + } + + if (isset($content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']])) { + list($params['results'], $smarty->_cache_info) = $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']]; + return true; + } + + if (!empty($smarty->cache_handler_func)) { + // use cache_handler function + call_user_func_array($smarty->cache_handler_func, + array('read', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null)); + } else { + // use local cache file + $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']); + $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id); + $params['results'] = $smarty->_read_file($_cache_file); + } + + if (empty($params['results'])) { + // nothing to parse (error?), regenerate cache + return false; + } + + $_contents = $params['results']; + $_info_start = strpos($_contents, "\n") + 1; + $_info_len = (int)substr($_contents, 0, $_info_start - 1); + $_cache_info = unserialize(substr($_contents, $_info_start, $_info_len)); + $params['results'] = substr($_contents, $_info_start + $_info_len); + + if ($smarty->caching == 2 && isset ($_cache_info['expires'])){ + // caching by expiration time + if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) { + // cache expired, regenerate + return false; + } + } else { + // caching by lifetime + if ($smarty->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $smarty->cache_lifetime)) { + // cache expired, regenerate + return false; + } + } + + if ($smarty->compile_check) { + $_params = array('get_source' => false, 'quiet'=>true); + foreach (array_keys($_cache_info['template']) as $_template_dep) { + $_params['resource_name'] = $_template_dep; + if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) { + // template file has changed, regenerate cache + return false; + } + } + + if (isset($_cache_info['config'])) { + $_params = array('resource_base_path' => $smarty->config_dir, 'get_source' => false, 'quiet'=>true); + foreach (array_keys($_cache_info['config']) as $_config_dep) { + $_params['resource_name'] = $_config_dep; + if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) { + // config file has changed, regenerate cache + return false; + } + } + } + } + + $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']] = array($params['results'], $_cache_info); + + $smarty->_cache_info = $_cache_info; + return true; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.rm_auto.php b/library/smarty/internals/core.rm_auto.php new file mode 100755 index 0000000..5174f02 --- /dev/null +++ b/library/smarty/internals/core.rm_auto.php @@ -0,0 +1,71 @@ + $params['auto_base'], + 'level' => 0, + 'exp_time' => $params['exp_time'] + ); + require_once(SMARTY_CORE_DIR . 'core.rmdir.php'); + $_res = smarty_core_rmdir($_params, $smarty); + } else { + $_tname = $smarty->_get_auto_filename($params['auto_base'], $params['auto_source'], $params['auto_id']); + + if(isset($params['auto_source'])) { + if (isset($params['extensions'])) { + $_res = false; + foreach ((array)$params['extensions'] as $_extension) + $_res |= $smarty->_unlink($_tname.$_extension, $params['exp_time']); + } else { + $_res = $smarty->_unlink($_tname, $params['exp_time']); + } + } elseif ($smarty->use_sub_dirs) { + $_params = array( + 'dirname' => $_tname, + 'level' => 1, + 'exp_time' => $params['exp_time'] + ); + require_once(SMARTY_CORE_DIR . 'core.rmdir.php'); + $_res = smarty_core_rmdir($_params, $smarty); + } else { + // remove matching file names + $_handle = opendir($params['auto_base']); + $_res = true; + while (false !== ($_filename = readdir($_handle))) { + if($_filename == '.' || $_filename == '..') { + continue; + } elseif (substr($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, 0, strlen($_tname)) == $_tname) { + $_res &= (bool)$smarty->_unlink($params['auto_base'] . DIRECTORY_SEPARATOR . $_filename, $params['exp_time']); + } + } + } + } + + return $_res; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.rmdir.php b/library/smarty/internals/core.rmdir.php new file mode 100755 index 0000000..3280ff7 --- /dev/null +++ b/library/smarty/internals/core.rmdir.php @@ -0,0 +1,54 @@ + keep root) + * WARNING: no tests, it will try to remove what you tell it! + * + * @param string $dirname + * @param integer $level + * @param integer $exp_time + * @return boolean + */ + +// $dirname, $level = 1, $exp_time = null + +function smarty_core_rmdir($params, &$smarty) +{ + if(!isset($params['level'])) { $params['level'] = 1; } + if(!isset($params['exp_time'])) { $params['exp_time'] = null; } + + if($_handle = @opendir($params['dirname'])) { + + while (false !== ($_entry = readdir($_handle))) { + if ($_entry != '.' && $_entry != '..') { + if (@is_dir($params['dirname'] . DIRECTORY_SEPARATOR . $_entry)) { + $_params = array( + 'dirname' => $params['dirname'] . DIRECTORY_SEPARATOR . $_entry, + 'level' => $params['level'] + 1, + 'exp_time' => $params['exp_time'] + ); + smarty_core_rmdir($_params, $smarty); + } + else { + $smarty->_unlink($params['dirname'] . DIRECTORY_SEPARATOR . $_entry, $params['exp_time']); + } + } + } + closedir($_handle); + } + + if ($params['level']) { + return @rmdir($params['dirname']); + } + return (bool)$_handle; + +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.run_insert_handler.php b/library/smarty/internals/core.run_insert_handler.php new file mode 100755 index 0000000..40e539f --- /dev/null +++ b/library/smarty/internals/core.run_insert_handler.php @@ -0,0 +1,71 @@ +debugging) { + $_params = array(); + $_debug_start_time = smarty_core_get_microtime($_params, $smarty); + } + + if ($smarty->caching) { + $_arg_string = serialize($params['args']); + $_name = $params['args']['name']; + if (!isset($smarty->_cache_info['insert_tags'][$_name])) { + $smarty->_cache_info['insert_tags'][$_name] = array('insert', + $_name, + $smarty->_plugins['insert'][$_name][1], + $smarty->_plugins['insert'][$_name][2], + !empty($params['args']['script']) ? true : false); + } + return $smarty->_smarty_md5."{insert_cache $_arg_string}".$smarty->_smarty_md5; + } else { + if (isset($params['args']['script'])) { + $_params = array('resource_name' => $smarty->_dequote($params['args']['script'])); + require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); + if(!smarty_core_get_php_resource($_params, $smarty)) { + return false; + } + + if ($_params['resource_type'] == 'file') { + $smarty->_include($_params['php_resource'], true); + } else { + $smarty->_eval($_params['php_resource']); + } + unset($params['args']['script']); + } + + $_funcname = $smarty->_plugins['insert'][$params['args']['name']][0]; + $_content = $_funcname($params['args'], $smarty); + if ($smarty->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $smarty->_smarty_debug_info[] = array('type' => 'insert', + 'filename' => 'insert_'.$params['args']['name'], + 'depth' => $smarty->_inclusion_depth, + 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time); + } + + if (!empty($params['args']["assign"])) { + $smarty->assign($params['args']["assign"], $_content); + } else { + return $_content; + } + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.smarty_include_php.php b/library/smarty/internals/core.smarty_include_php.php new file mode 100755 index 0000000..de03f93 --- /dev/null +++ b/library/smarty/internals/core.smarty_include_php.php @@ -0,0 +1,50 @@ + $params['smarty_file']); + require_once(SMARTY_CORE_DIR . 'core.get_php_resource.php'); + smarty_core_get_php_resource($_params, $smarty); + $_smarty_resource_type = $_params['resource_type']; + $_smarty_php_resource = $_params['php_resource']; + + if (!empty($params['smarty_assign'])) { + ob_start(); + if ($_smarty_resource_type == 'file') { + $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']); + } else { + $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']); + } + $smarty->assign($params['smarty_assign'], ob_get_contents()); + ob_end_clean(); + } else { + if ($_smarty_resource_type == 'file') { + $smarty->_include($_smarty_php_resource, $params['smarty_once'], $params['smarty_include_vars']); + } else { + $smarty->_eval($_smarty_php_resource, $params['smarty_include_vars']); + } + } +} + + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.write_cache_file.php b/library/smarty/internals/core.write_cache_file.php new file mode 100755 index 0000000..4cf3601 --- /dev/null +++ b/library/smarty/internals/core.write_cache_file.php @@ -0,0 +1,96 @@ +_cache_info['timestamp'] = time(); + if ($smarty->cache_lifetime > -1){ + // expiration set + $smarty->_cache_info['expires'] = $smarty->_cache_info['timestamp'] + $smarty->cache_lifetime; + } else { + // cache will never expire + $smarty->_cache_info['expires'] = -1; + } + + // collapse nocache.../nocache-tags + if (preg_match_all('!\{(/?)nocache\:[0-9a-f]{32}#\d+\}!', $params['results'], $match, PREG_PATTERN_ORDER)) { + // remove everything between every pair of outermost noache.../nocache-tags + // and replace it by a single nocache-tag + // this new nocache-tag will be replaced by dynamic contents in + // smarty_core_process_compiled_includes() on a cache-read + + $match_count = count($match[0]); + $results = preg_split('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!', $params['results'], -1, PREG_SPLIT_DELIM_CAPTURE); + + $level = 0; + $j = 0; + for ($i=0, $results_count = count($results); $i < $results_count && $j < $match_count; $i++) { + if ($results[$i] == $match[0][$j]) { + // nocache tag + if ($match[1][$j]) { // closing tag + $level--; + unset($results[$i]); + } else { // opening tag + if ($level++ > 0) unset($results[$i]); + } + $j++; + } elseif ($level > 0) { + unset($results[$i]); + } + } + $params['results'] = implode('', $results); + } + $smarty->_cache_info['cache_serials'] = $smarty->_cache_serials; + + // prepend the cache header info into cache file + $_cache_info = serialize($smarty->_cache_info); + $params['results'] = strlen($_cache_info) . "\n" . $_cache_info . $params['results']; + + if (!empty($smarty->cache_handler_func)) { + // use cache_handler function + call_user_func_array($smarty->cache_handler_func, + array('write', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null)); + } else { + // use local cache file + + if(!@is_writable($smarty->cache_dir)) { + // cache_dir not writable, see if it exists + if(!@is_dir($smarty->cache_dir)) { + $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR); + return false; + } + $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR); + return false; + } + + $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']); + $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id); + $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true); + require_once(SMARTY_CORE_DIR . 'core.write_file.php'); + smarty_core_write_file($_params, $smarty); + return true; + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.write_compiled_include.php b/library/smarty/internals/core.write_compiled_include.php new file mode 100755 index 0000000..9cf8929 --- /dev/null +++ b/library/smarty/internals/core.write_compiled_include.php @@ -0,0 +1,91 @@ +caching && \!\$this->_cache_including\) \{ echo \'\{nocache\:('.$params['cache_serial'].')#(\d+)\}\'; \};'; + $_tag_end = 'if \(\$this->caching && \!\$this->_cache_including\) \{ echo \'\{/nocache\:(\\2)#(\\3)\}\'; \};'; + + preg_match_all('!('.$_tag_start.'(.*)'.$_tag_end.')!Us', + $params['compiled_content'], $_match_source, PREG_SET_ORDER); + + // no nocache-parts found: done + if (count($_match_source)==0) return; + + // convert the matched php-code to functions + $_include_compiled = "_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; + $_include_compiled .= " compiled from " . strtr(urlencode($params['resource_name']), array('%2F'=>'/', '%3A'=>':')) . " */\n\n"; + + $_compile_path = $params['include_file_path']; + + $smarty->_cache_serials[$_compile_path] = $params['cache_serial']; + $_include_compiled .= "\$this->_cache_serials['".$_compile_path."'] = '".$params['cache_serial']."';\n\n?>"; + + $_include_compiled .= $params['plugins_code']; + $_include_compiled .= "= 5.0) ? '_smarty' : 'this'; + for ($_i = 0, $_for_max = count($_match_source); $_i < $_for_max; $_i++) { + $_match =& $_match_source[$_i]; + $source = $_match[4]; + if ($this_varname == '_smarty') { + /* rename $this to $_smarty in the sourcecode */ + $tokens = token_get_all('\n"; + + $_params = array('filename' => $_compile_path, + 'contents' => $_include_compiled, 'create_dirs' => true); + + require_once(SMARTY_CORE_DIR . 'core.write_file.php'); + smarty_core_write_file($_params, $smarty); + return true; +} + + +?> diff --git a/library/smarty/internals/core.write_compiled_resource.php b/library/smarty/internals/core.write_compiled_resource.php new file mode 100755 index 0000000..d0e5648 --- /dev/null +++ b/library/smarty/internals/core.write_compiled_resource.php @@ -0,0 +1,35 @@ +compile_dir)) { + // compile_dir not writable, see if it exists + if(!@is_dir($smarty->compile_dir)) { + $smarty->trigger_error('the $compile_dir \'' . $smarty->compile_dir . '\' does not exist, or is not a directory.', E_USER_ERROR); + return false; + } + $smarty->trigger_error('unable to write to $compile_dir \'' . realpath($smarty->compile_dir) . '\'. Be sure $compile_dir is writable by the web server user.', E_USER_ERROR); + return false; + } + + $_params = array('filename' => $params['compile_path'], 'contents' => $params['compiled_content'], 'create_dirs' => true); + require_once(SMARTY_CORE_DIR . 'core.write_file.php'); + smarty_core_write_file($_params, $smarty); + return true; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/internals/core.write_file.php b/library/smarty/internals/core.write_file.php new file mode 100755 index 0000000..333b762 --- /dev/null +++ b/library/smarty/internals/core.write_file.php @@ -0,0 +1,54 @@ + $_dirname); + require_once(SMARTY_CORE_DIR . 'core.create_dir_structure.php'); + smarty_core_create_dir_structure($_params, $smarty); + } + + // write to tmp file, then rename it to avoid + // file locking race condition + $_tmp_file = tempnam($_dirname, 'wrt'); + + if (!($fd = @fopen($_tmp_file, 'wb'))) { + $_tmp_file = $_dirname . DIRECTORY_SEPARATOR . uniqid('wrt'); + if (!($fd = @fopen($_tmp_file, 'wb'))) { + $smarty->trigger_error("problem writing temporary file '$_tmp_file'"); + return false; + } + } + + fwrite($fd, $params['contents']); + fclose($fd); + + // Delete the file if it allready exists (this is needed on Win, + // because it cannot overwrite files with rename() + if (file_exists($params['filename'])) { + @unlink($params['filename']); + } + @rename($_tmp_file, $params['filename']); + @chmod($params['filename'], $smarty->_file_perms); + + return true; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/block.textformat.php b/library/smarty/plugins/block.textformat.php new file mode 100755 index 0000000..2a37423 --- /dev/null +++ b/library/smarty/plugins/block.textformat.php @@ -0,0 +1,103 @@ + + * Name: textformat
    + * Purpose: format text a certain way with preset styles + * or custom wrap/indent settings
    + * @link http://smarty.php.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * @param array + *
    + * Params:   style: string (email)
    + *           indent: integer (0)
    + *           wrap: integer (80)
    + *           wrap_char string ("\n")
    + *           indent_char: string (" ")
    + *           wrap_boundary: boolean (true)
    + * 
    + * @author Monte Ohrt + * @param string contents of the block + * @param Smarty clever simulation of a method + * @return string string $content re-formatted + */ +function smarty_block_textformat($params, $content, &$smarty) +{ + if (is_null($content)) { + return; + } + + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string)$_val; + break; + + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int)$_val; + break; + + case 'wrap_cut': + $$_key = (bool)$_val; + break; + + default: + $smarty->trigger_error("textformat: unknown attribute '$_key'"); + } + } + + if ($style == 'email') { + $wrap = 72; + } + + // split into paragraphs + $_paragraphs = preg_split('![\r\n][\r\n]!',$content); + $_output = ''; + + for($_x = 0, $_y = count($_paragraphs); $_x < $_y; $_x++) { + if ($_paragraphs[$_x] == '') { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraphs[$_x] = preg_replace(array('!\s+!','!(^\s+)|(\s+$)!'), array(' ',''), $_paragraphs[$_x]); + // indent first line + if($indent_first > 0) { + $_paragraphs[$_x] = str_repeat($indent_char, $indent_first) . $_paragraphs[$_x]; + } + // wordwrap sentences + $_paragraphs[$_x] = wordwrap($_paragraphs[$_x], $wrap - $indent, $wrap_char, $wrap_cut); + // indent lines + if($indent > 0) { + $_paragraphs[$_x] = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraphs[$_x]); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + + return $assign ? $smarty->assign($assign, $_output) : $_output; + +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/compiler.assign.php b/library/smarty/plugins/compiler.assign.php new file mode 100755 index 0000000..25970f1 --- /dev/null +++ b/library/smarty/plugins/compiler.assign.php @@ -0,0 +1,40 @@ + + * Name: assign
    + * Purpose: assign a value to a template variable + * @link http://smarty.php.net/manual/en/language.custom.functions.php#LANGUAGE.FUNCTION.ASSIGN {assign} + * (Smarty online manual) + * @author Monte Ohrt (initial author) + * @auther messju mohr (conversion to compiler function) + * @param string containing var-attribute and value-attribute + * @param Smarty_Compiler + */ +function smarty_compiler_assign($tag_attrs, &$compiler) +{ + $_params = $compiler->_parse_attrs($tag_attrs); + + if (!isset($_params['var'])) { + $compiler->_syntax_error("assign: missing 'var' parameter", E_USER_WARNING); + return; + } + + if (!isset($_params['value'])) { + $compiler->_syntax_error("assign: missing 'value' parameter", E_USER_WARNING); + return; + } + + return "\$this->assign({$_params['var']}, {$_params['value']});"; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.assign_debug_info.php b/library/smarty/plugins/function.assign_debug_info.php new file mode 100755 index 0000000..cc2c14b --- /dev/null +++ b/library/smarty/plugins/function.assign_debug_info.php @@ -0,0 +1,40 @@ + + * Name: assign_debug_info
    + * Purpose: assign debug info to the template
    + * @author Monte Ohrt + * @param array unused in this plugin, this plugin uses {@link Smarty::$_config}, + * {@link Smarty::$_tpl_vars} and {@link Smarty::$_smarty_debug_info} + * @param Smarty + */ +function smarty_function_assign_debug_info($params, &$smarty) +{ + $assigned_vars = $smarty->_tpl_vars; + ksort($assigned_vars); + if (@is_array($smarty->_config[0])) { + $config_vars = $smarty->_config[0]; + ksort($config_vars); + $smarty->assign("_debug_config_keys", array_keys($config_vars)); + $smarty->assign("_debug_config_vals", array_values($config_vars)); + } + + $included_templates = $smarty->_smarty_debug_info; + + $smarty->assign("_debug_keys", array_keys($assigned_vars)); + $smarty->assign("_debug_vals", array_values($assigned_vars)); + + $smarty->assign("_debug_tpls", $included_templates); +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.config_load.php b/library/smarty/plugins/function.config_load.php new file mode 100755 index 0000000..062c356 --- /dev/null +++ b/library/smarty/plugins/function.config_load.php @@ -0,0 +1,142 @@ + + * Name: config_load
    + * Purpose: load config file vars + * @link http://smarty.php.net/manual/en/language.function.config.load.php {config_load} + * (Smarty online manual) + * @author Monte Ohrt + * @author messju mohr (added use of resources) + * @param array Format: + *
    + * array('file' => required config file name,
    + *       'section' => optional config file section to load
    + *       'scope' => local/parent/global
    + *       'global' => overrides scope, setting to parent if true)
    + * 
    + * @param Smarty + */ +function smarty_function_config_load($params, &$smarty) +{ + if ($smarty->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $_debug_start_time = smarty_core_get_microtime($_params, $smarty); + } + + $_file = isset($params['file']) ? $smarty->_dequote($params['file']) : null; + $_section = isset($params['section']) ? $smarty->_dequote($params['section']) : null; + $_scope = isset($params['scope']) ? $smarty->_dequote($params['scope']) : 'global'; + $_global = isset($params['global']) ? $smarty->_dequote($params['global']) : false; + + if (!isset($_file) || strlen($_file) == 0) { + $smarty->trigger_error("missing 'file' attribute in config_load tag", E_USER_ERROR, __FILE__, __LINE__); + } + + if (isset($_scope)) { + if ($_scope != 'local' && + $_scope != 'parent' && + $_scope != 'global') { + $smarty->trigger_error("invalid 'scope' attribute value", E_USER_ERROR, __FILE__, __LINE__); + } + } else { + if ($_global) { + $_scope = 'parent'; + } else { + $_scope = 'local'; + } + } + + $_params = array('resource_name' => $_file, + 'resource_base_path' => $smarty->config_dir, + 'get_source' => false); + $smarty->_parse_resource_name($_params); + $_file_path = $_params['resource_type'] . ':' . $_params['resource_name']; + if (isset($_section)) + $_compile_file = $smarty->_get_compile_path($_file_path.'|'.$_section); + else + $_compile_file = $smarty->_get_compile_path($_file_path); + + if($smarty->force_compile || !file_exists($_compile_file)) { + $_compile = true; + } elseif ($smarty->compile_check) { + $_params = array('resource_name' => $_file, + 'resource_base_path' => $smarty->config_dir, + 'get_source' => false); + $_compile = $smarty->_fetch_resource_info($_params) && + $_params['resource_timestamp'] > filemtime($_compile_file); + } else { + $_compile = false; + } + + if($_compile) { + // compile config file + if(!is_object($smarty->_conf_obj)) { + require_once SMARTY_DIR . $smarty->config_class . '.class.php'; + $smarty->_conf_obj = new $smarty->config_class(); + $smarty->_conf_obj->overwrite = $smarty->config_overwrite; + $smarty->_conf_obj->booleanize = $smarty->config_booleanize; + $smarty->_conf_obj->read_hidden = $smarty->config_read_hidden; + $smarty->_conf_obj->fix_newlines = $smarty->config_fix_newlines; + } + + $_params = array('resource_name' => $_file, + 'resource_base_path' => $smarty->config_dir, + $_params['get_source'] = true); + if (!$smarty->_fetch_resource_info($_params)) { + return; + } + $smarty->_conf_obj->set_file_contents($_file, $_params['source_content']); + $_config_vars = array_merge($smarty->_conf_obj->get($_file), + $smarty->_conf_obj->get($_file, $_section)); + if(function_exists('var_export')) { + $_output = ''; + } else { + $_output = ''\\\'', '\\'=>'\\\\')) . '\'); ?>'; + } + $_params = (array('compile_path' => $_compile_file, 'compiled_content' => $_output, 'resource_timestamp' => $_params['resource_timestamp'])); + require_once(SMARTY_CORE_DIR . 'core.write_compiled_resource.php'); + smarty_core_write_compiled_resource($_params, $smarty); + } else { + include($_compile_file); + } + + if ($smarty->caching) { + $smarty->_cache_info['config'][$_file] = true; + } + + $smarty->_config[0]['vars'] = @array_merge($smarty->_config[0]['vars'], $_config_vars); + $smarty->_config[0]['files'][$_file] = true; + + if ($_scope == 'parent') { + $smarty->_config[1]['vars'] = @array_merge($smarty->_config[1]['vars'], $_config_vars); + $smarty->_config[1]['files'][$_file] = true; + } else if ($_scope == 'global') { + for ($i = 1, $for_max = count($smarty->_config); $i < $for_max; $i++) { + $smarty->_config[$i]['vars'] = @array_merge($smarty->_config[$i]['vars'], $_config_vars); + $smarty->_config[$i]['files'][$_file] = true; + } + } + + if ($smarty->debugging) { + $_params = array(); + require_once(SMARTY_CORE_DIR . 'core.get_microtime.php'); + $smarty->_smarty_debug_info[] = array('type' => 'config', + 'filename' => $_file.' ['.$_section.'] '.$_scope, + 'depth' => $smarty->_inclusion_depth, + 'exec_time' => smarty_core_get_microtime($_params, $smarty) - $_debug_start_time); + } + +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.counter.php b/library/smarty/plugins/function.counter.php new file mode 100755 index 0000000..e8937d1 --- /dev/null +++ b/library/smarty/plugins/function.counter.php @@ -0,0 +1,80 @@ + + * Name: counter
    + * Purpose: print out a counter value + * @author Monte Ohrt + * @link http://smarty.php.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * @param array parameters + * @param Smarty + * @return string|null + */ +function smarty_function_counter($params, &$smarty) +{ + static $counters = array(); + + $name = (isset($params['name'])) ? $params['name'] : 'default'; + if (!isset($counters[$name])) { + $counters[$name] = array( + 'start'=>1, + 'skip'=>1, + 'direction'=>'up', + 'count'=>1 + ); + } + $counter =& $counters[$name]; + + if (isset($params['start'])) { + $counter['start'] = $counter['count'] = (int)$params['start']; + } + + if (!empty($params['assign'])) { + $counter['assign'] = $params['assign']; + } + + if (isset($counter['assign'])) { + $smarty->assign($counter['assign'], $counter['count']); + } + + if (isset($params['print'])) { + $print = (bool)$params['print']; + } else { + $print = empty($counter['assign']); + } + + if ($print) { + $retval = $counter['count']; + } else { + $retval = null; + } + + if (isset($params['skip'])) { + $counter['skip'] = $params['skip']; + } + + if (isset($params['direction'])) { + $counter['direction'] = $params['direction']; + } + + if ($counter['direction'] == "down") + $counter['count'] -= $counter['skip']; + else + $counter['count'] += $counter['skip']; + + return $retval; + +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.cycle.php b/library/smarty/plugins/function.cycle.php new file mode 100755 index 0000000..1a04af9 --- /dev/null +++ b/library/smarty/plugins/function.cycle.php @@ -0,0 +1,102 @@ + + * Name: cycle
    + * Date: May 3, 2002
    + * Purpose: cycle through given values
    + * Input: + * - name = name of cycle (optional) + * - values = comma separated list of values to cycle, + * or an array of values to cycle + * (this can be left out for subsequent calls) + * - reset = boolean - resets given var to true + * - print = boolean - print var or not. default is true + * - advance = boolean - whether or not to advance the cycle + * - delimiter = the value delimiter, default is "," + * - assign = boolean, assigns to template var instead of + * printed. + * + * Examples:
    + *
    + * {cycle values="#eeeeee,#d0d0d0d"}
    + * {cycle name=row values="one,two,three" reset=true}
    + * {cycle name=row}
    + * 
    + * @link http://smarty.php.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * @param array + * @param Smarty + * @return string|null + */ +function smarty_function_cycle($params, &$smarty) +{ + static $cycle_vars; + + $name = (empty($params['name'])) ? 'default' : $params['name']; + $print = (isset($params['print'])) ? (bool)$params['print'] : true; + $advance = (isset($params['advance'])) ? (bool)$params['advance'] : true; + $reset = (isset($params['reset'])) ? (bool)$params['reset'] : false; + + if (!in_array('values', array_keys($params))) { + if(!isset($cycle_vars[$name]['values'])) { + $smarty->trigger_error("cycle: missing 'values' parameter"); + return; + } + } else { + if(isset($cycle_vars[$name]['values']) + && $cycle_vars[$name]['values'] != $params['values'] ) { + $cycle_vars[$name]['index'] = 0; + } + $cycle_vars[$name]['values'] = $params['values']; + } + + $cycle_vars[$name]['delimiter'] = (isset($params['delimiter'])) ? $params['delimiter'] : ','; + + if(is_array($cycle_vars[$name]['values'])) { + $cycle_array = $cycle_vars[$name]['values']; + } else { + $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']); + } + + if(!isset($cycle_vars[$name]['index']) || $reset ) { + $cycle_vars[$name]['index'] = 0; + } + + if (isset($params['assign'])) { + $print = false; + $smarty->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); + } + + if($print) { + $retval = $cycle_array[$cycle_vars[$name]['index']]; + } else { + $retval = null; + } + + if($advance) { + if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) { + $cycle_vars[$name]['index'] = 0; + } else { + $cycle_vars[$name]['index']++; + } + } + + return $retval; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.debug.php b/library/smarty/plugins/function.debug.php new file mode 100755 index 0000000..de09b30 --- /dev/null +++ b/library/smarty/plugins/function.debug.php @@ -0,0 +1,35 @@ + + * Name: debug
    + * Date: July 1, 2002
    + * Purpose: popup debug window + * @link http://smarty.php.net/manual/en/language.function.debug.php {debug} + * (Smarty online manual) + * @author Monte Ohrt + * @version 1.0 + * @param array + * @param Smarty + * @return string output from {@link Smarty::_generate_debug_output()} + */ +function smarty_function_debug($params, &$smarty) +{ + if (isset($params['output'])) { + $smarty->assign('_smarty_debug_output', $params['output']); + } + require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php'); + return smarty_core_display_debug_console(null, $smarty); +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.eval.php b/library/smarty/plugins/function.eval.php new file mode 100755 index 0000000..3bb8cbb --- /dev/null +++ b/library/smarty/plugins/function.eval.php @@ -0,0 +1,49 @@ + + * Name: eval
    + * Purpose: evaluate a template variable as a template
    + * @link http://smarty.php.net/manual/en/language.function.eval.php {eval} + * (Smarty online manual) + * @author Monte Ohrt + * @param array + * @param Smarty + */ +function smarty_function_eval($params, &$smarty) +{ + + if (!isset($params['var'])) { + $smarty->trigger_error("eval: missing 'var' parameter"); + return; + } + + if($params['var'] == '') { + return; + } + + $smarty->_compile_source('evaluated template', $params['var'], $_var_compiled); + + ob_start(); + $smarty->_eval('?>' . $_var_compiled); + $_contents = ob_get_contents(); + ob_end_clean(); + + if (!empty($params['assign'])) { + $smarty->assign($params['assign'], $_contents); + } else { + return $_contents; + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.fetch.php b/library/smarty/plugins/function.fetch.php new file mode 100755 index 0000000..0d5ce7f --- /dev/null +++ b/library/smarty/plugins/function.fetch.php @@ -0,0 +1,221 @@ + + * Name: fetch
    + * Purpose: fetch file, web or ftp data and display results + * @link http://smarty.php.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) + * @author Monte Ohrt + * @param array + * @param Smarty + * @return string|null if the assign parameter is passed, Smarty assigns the + * result to a template variable + */ +function smarty_function_fetch($params, &$smarty) +{ + if (empty($params['file'])) { + $smarty->_trigger_fatal_error("[plugin] parameter 'file' cannot be empty"); + return; + } + + $content = ''; + if ($smarty->security && !preg_match('!^(http|ftp)://!i', $params['file'])) { + $_params = array('resource_type' => 'file', 'resource_name' => $params['file']); + require_once(SMARTY_CORE_DIR . 'core.is_secure.php'); + if(!smarty_core_is_secure($_params, $smarty)) { + $smarty->_trigger_fatal_error('[plugin] (secure mode) fetch \'' . $params['file'] . '\' is not allowed'); + return; + } + + // fetch the file + if($fp = @fopen($params['file'],'r')) { + while(!feof($fp)) { + $content .= fgets ($fp,4096); + } + fclose($fp); + } else { + $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] . '\''); + return; + } + } else { + // not a local file + if(preg_match('!^http://!i',$params['file'])) { + // http fetch + if($uri_parts = parse_url($params['file'])) { + // set defaults + $host = $server_name = $uri_parts['host']; + $timeout = 30; + $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + $agent = "Smarty Template Engine ".$smarty->_version; + $referer = ""; + $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; + $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; + $_is_proxy = false; + if(empty($uri_parts['port'])) { + $port = 80; + } else { + $port = $uri_parts['port']; + } + if(!empty($uri_parts['user'])) { + $user = $uri_parts['user']; + } + if(!empty($uri_parts['pass'])) { + $pass = $uri_parts['pass']; + } + // loop through parameters, setup headers + foreach($params as $param_key => $param_value) { + switch($param_key) { + case "file": + case "assign": + case "assign_headers": + break; + case "user": + if(!empty($param_value)) { + $user = $param_value; + } + break; + case "pass": + if(!empty($param_value)) { + $pass = $param_value; + } + break; + case "accept": + if(!empty($param_value)) { + $accept = $param_value; + } + break; + case "header": + if(!empty($param_value)) { + if(!preg_match('![\w\d-]+: .+!',$param_value)) { + $smarty->_trigger_fatal_error("[plugin] invalid header format '".$param_value."'"); + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case "proxy_host": + if(!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case "proxy_port": + if(!preg_match('!\D!', $param_value)) { + $proxy_port = (int) $param_value; + } else { + $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'"); + return; + } + break; + case "agent": + if(!empty($param_value)) { + $agent = $param_value; + } + break; + case "referer": + if(!empty($param_value)) { + $referer = $param_value; + } + break; + case "timeout": + if(!preg_match('!\D!', $param_value)) { + $timeout = (int) $param_value; + } else { + $smarty->_trigger_fatal_error("[plugin] invalid value for attribute '".$param_key."'"); + return; + } + break; + default: + $smarty->_trigger_fatal_error("[plugin] unrecognized attribute '".$param_key."'"); + return; + } + } + if(!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout); + } else { + $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout); + } + + if(!$fp) { + $smarty->_trigger_fatal_error("[plugin] unable to fetch: $errstr ($errno)"); + return; + } else { + if($_is_proxy) { + fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if(!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if(!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if(!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if(!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if(isset($extra_headers) && is_array($extra_headers)) { + foreach($extra_headers as $curr_header) { + fputs($fp, $curr_header."\r\n"); + } + } + if(!empty($user) && !empty($pass)) { + fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n"); + } + + fputs($fp, "\r\n"); + while(!feof($fp)) { + $content .= fgets($fp,4096); + } + fclose($fp); + $csplit = split("\r\n\r\n",$content,2); + + $content = $csplit[1]; + + if(!empty($params['assign_headers'])) { + $smarty->assign($params['assign_headers'],split("\r\n",$csplit[0])); + } + } + } else { + $smarty->_trigger_fatal_error("[plugin] unable to parse URL, check syntax"); + return; + } + } else { + // ftp fetch + if($fp = @fopen($params['file'],'r')) { + while(!feof($fp)) { + $content .= fgets ($fp,4096); + } + fclose($fp); + } else { + $smarty->_trigger_fatal_error('[plugin] fetch cannot read file \'' . $params['file'] .'\''); + return; + } + } + + } + + + if (!empty($params['assign'])) { + $smarty->assign($params['assign'],$content); + } else { + return $content; + } +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.html_checkboxes.php b/library/smarty/plugins/function.html_checkboxes.php new file mode 100755 index 0000000..45a161b --- /dev/null +++ b/library/smarty/plugins/function.html_checkboxes.php @@ -0,0 +1,143 @@ + + * Type: function
    + * Name: html_checkboxes
    + * Date: 24.Feb.2003
    + * Purpose: Prints out a list of checkbox input types
    + * Input:
    + * - name (optional) - string default "checkbox" + * - values (required) - array + * - options (optional) - associative array + * - checked (optional) - array default not set + * - separator (optional) - ie
    or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * Examples: + *
    + * {html_checkboxes values=$ids output=$names}
    + * {html_checkboxes values=$ids name='box' separator='
    ' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
    ' output=$names} + *
    + * @link http://smarty.php.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * @param array + * @param Smarty + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_checkboxes($params, &$smarty) +{ + require_once $smarty->_get_plugin_filepath('shared','escape_special_chars'); + + $name = 'checkbox'; + $values = null; + $options = null; + $selected = null; + $separator = ''; + $labels = true; + $output = null; + + $extra = ''; + + foreach($params as $_key => $_val) { + switch($_key) { + case 'name': + case 'separator': + $$_key = $_val; + break; + + case 'labels': + $$_key = (bool)$_val; + break; + + case 'options': + $$_key = (array)$_val; + break; + + case 'values': + case 'output': + $$_key = array_values((array)$_val); + break; + + case 'checked': + case 'selected': + $selected = array_map('strval', array_values((array)$_val)); + break; + + case 'checkboxes': + $smarty->trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING); + $options = (array)$_val; + break; + + case 'assign': + break; + + default: + if(!is_array($_val)) { + $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; + } else { + $smarty->trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (!isset($options) && !isset($values)) + return ''; /* raise error here? */ + + settype($selected, 'array'); + $_html_result = array(); + + if (isset($options)) { + + foreach ($options as $_key=>$_val) + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels); + + + } else { + foreach ($values as $_i=>$_key) { + $_val = isset($output[$_i]) ? $output[$_i] : ''; + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels); + } + + } + + if(!empty($params['assign'])) { + $smarty->assign($params['assign'], $_html_result); + } else { + return implode("\n",$_html_result); + } + +} + +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels) { + $_output = ''; + if ($labels) $_output .= ''; + $_output .= $separator; + + return $_output; +} + +?> diff --git a/library/smarty/plugins/function.html_image.php b/library/smarty/plugins/function.html_image.php new file mode 100755 index 0000000..d758ebe --- /dev/null +++ b/library/smarty/plugins/function.html_image.php @@ -0,0 +1,142 @@ + + * Name: html_image
    + * Date: Feb 24, 2003
    + * Purpose: format HTML tags for the image
    + * Input:
    + * - file = file (and path) of image (required) + * - height = image height (optional, default actual height) + * - width = image width (optional, default actual width) + * - basedir = base directory for absolute paths, default + * is environment variable DOCUMENT_ROOT + * - path_prefix = prefix for path output (optional, default empty) + * + * Examples: {html_image file="/images/masthead.gif"} + * Output: + * @link http://smarty.php.net/manual/en/language.function.html.image.php {html_image} + * (Smarty online manual) + * @author Monte Ohrt + * @author credits to Duda - wrote first image function + * in repository, helped with lots of functionality + * @version 1.0 + * @param array + * @param Smarty + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_image($params, &$smarty) +{ + require_once $smarty->_get_plugin_filepath('shared','escape_special_chars'); + + $alt = ''; + $file = ''; + $height = ''; + $width = ''; + $extra = ''; + $prefix = ''; + $suffix = ''; + $path_prefix = ''; + $server_vars = ($smarty->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS']; + $basedir = isset($server_vars['DOCUMENT_ROOT']) ? $server_vars['DOCUMENT_ROOT'] : ''; + foreach($params as $_key => $_val) { + switch($_key) { + case 'file': + case 'height': + case 'width': + case 'dpi': + case 'path_prefix': + case 'basedir': + $$_key = $_val; + break; + + case 'alt': + if(!is_array($_val)) { + $$_key = smarty_function_escape_special_chars($_val); + } else { + $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + + case 'link': + case 'href': + $prefix = ''; + $suffix = ''; + break; + + default: + if(!is_array($_val)) { + $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; + } else { + $smarty->trigger_error("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (empty($file)) { + $smarty->trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE); + return; + } + + if (substr($file,0,1) == '/') { + $_image_path = $basedir . $file; + } else { + $_image_path = $file; + } + + if(!isset($params['width']) || !isset($params['height'])) { + if(!$_image_data = @getimagesize($_image_path)) { + if(!file_exists($_image_path)) { + $smarty->trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE); + return; + } else if(!is_readable($_image_path)) { + $smarty->trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE); + return; + } else { + $smarty->trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE); + return; + } + } + if ($smarty->security && + ($_params = array('resource_type' => 'file', 'resource_name' => $_image_path)) && + (require_once(SMARTY_CORE_DIR . 'core.is_secure.php')) && + (!smarty_core_is_secure($_params, $smarty)) ) { + $smarty->trigger_error("html_image: (secure) '$_image_path' not in secure directory", E_USER_NOTICE); + } + + if(!isset($params['width'])) { + $width = $_image_data[0]; + } + if(!isset($params['height'])) { + $height = $_image_data[1]; + } + + } + + if(isset($params['dpi'])) { + if(strstr($server_vars['HTTP_USER_AGENT'], 'Mac')) { + $dpi_default = 72; + } else { + $dpi_default = 96; + } + $_resize = $dpi_default/$params['dpi']; + $width = round($width * $_resize); + $height = round($height * $_resize); + } + + return $prefix . ''.$alt.'' . $suffix; +} + +/* vim: set expandtab: */ + +?> diff --git a/library/smarty/plugins/function.html_options.php b/library/smarty/plugins/function.html_options.php new file mode 100755 index 0000000..f84b631 --- /dev/null +++ b/library/smarty/plugins/function.html_options.php @@ -0,0 +1,122 @@ + + * Name: html_options
    + * Input:
    + * - name (optional) - string default "select" + * - values (required if no options supplied) - array + * - options (required if no values supplied) - associative array + * - selected (optional) - string default not set + * - output (required if not options supplied) - array + * Purpose: Prints the list of