/** * Initialize a new AnewtForm instance. * * Do not forget to call this method when you override the constructor in * a subclass, i.e. call <code>parent::__construct()</code>. */ function __construct() { $this->_seed(array('method' => ANEWT_FORM_METHOD_POST, 'action' => AnewtRequest::relative_url(), 'description' => null, 'error' => null, 'autocomplete' => null)); /* Automatic id for classes extending AnewtForm */ $class_name = get_class($this); if ($class_name != 'AnewtForm') { $this->id = sprintf('form-%s', str_strip_suffix(strtolower($class_name), 'form')); } }
/** * Constructor for a new SmartyTemplateRenderer instance * * \param $basetemplate Template name (optional, default is null) * \param $directories Directories to use (optional, see SmartyTemplate documentation for more information) * \param $mode Default render mode (optional, default is null) * * \see SmartyTemplate */ function SmartyTemplateRenderer($basetemplate = null, $directories = null, $mode = null) { /* Handle basetemplate parameter */ if (is_null($basetemplate)) { /* Try to use the class name. Note that the __CLASS__ constant does * not work for inherited classes, which is what we need... :( */ $basetemplate = str_strip_suffix(strtolower(get_class($this)), 'renderer'); } assert('is_string($basetemplate)'); assert('strlen($basetemplate) > 0'); $this->set('basetemplate', $basetemplate); /* Handle mode parameter */ if (!is_null($mode)) { assert('is_string($mode)'); $this->set('mode', $mode); } /* Call parent constructor */ parent::SmartyTemplate(null, $directories); }
/** * Create a simple include function that loads modules. Call this method from * a default include file to have a anewt_include() like function for your own * libraries. * * \param $name * Name of the function. This name will be suffixed with _include, e.g. * myproject will result in the function myproject_include() being defined * \param $path * The file path to use for inclusion * \param $file_suffix * Filename suffix (optional, default to .lib.php) * \param $main_file * Default filename in case the argument to the include function is * a directory (optional, default to main) * * \see anewt_include */ function create_include_function($name, $path, $file_suffix = '.lib.php', $main_file = 'main') { assert('is_string($name)'); assert('is_string($path)'); assert('is_string($file_suffix)'); assert('is_string($main_file)'); assert('is_dir($path)'); $function_name = sprintf('%s_include', $name); $path = str_strip_suffix($path, '/'); assert('function_exists($function_name) === false'); $function_definition = str_replace(array('@@FUNC@@', '@@PATH@@', '@@MAIN@@', '@@SUFFIX@@'), array($function_name, $path, $main_file, $file_suffix), 'function @@FUNC@@($module_or_file) { $args = func_get_args(); foreach ($args as $module_or_file) { $filename = \'@@PATH@@/\' . str_strip_suffix($module_or_file, \'/\'); if (is_dir($filename)) $filename .= \'/@@MAIN@@\'; $filename .= \'@@SUFFIX@@\'; require_once $filename; } }'); /* Evaluate the function definition */ eval($function_definition); }
/** * Add a prefix to the list of prefix mappings. Call this method from your * setup_mappings() method. * * \param $prefix * The url prefix to match * * \param $module_name * The module name passed to load_module() (optional, specify null if you * don't need it) * * \param $class_name * The class name of the dispatcher to instantiate and invoke. * * \see MultiURLDispatcher::setup_mappings * \see MultiURLDispatcher::load_module */ function add_prefix($prefix, $module_name = null, $class_name) { assert('is_string($prefix)'); assert('is_string($class_name)'); assert('is_null($module_name) || is_string($module_name)'); /* Make sure the prefix does not start or end with a / character */ $prefix = str_strip_prefix($prefix, '/'); $prefix = str_strip_suffix($prefix, '/'); /* Just store a list of mappings */ $this->prefix_to_dispatcher_mapping[$prefix] = array($module_name, $class_name); }
/** * Dispatch an URL to the correct handlers. This method does the actual * magic, such as url parsing, matching and command invocation. You can * optionally provide a custom url and tell the dispatcher that some parts * of the url should be skipped when handling this request. * * \param $url * The url to dispatch (optional, defaults to null). Omit this value (or * provide null) to use the current request url. * * \param $skip_path_components * The number of path components to skip when handling this request * (optional, defaults to null). This is useful if you want to skip some * prefix present in all urls, such as ~username. If you don't specify * this parameter the value of <code>$_SERVER['PHP_SELF']</code> will be * used to figure out how many components to skip. */ function dispatch($url = null, $skip_path_components = null) { if (is_null($skip_path_components)) { /* Figure out the current script location. This is most likely * the script living in the document root calling this method. Use * the directory path component of this file to figure out how many * path components should be skipped. */ $dir_url = dirname($_SERVER['PHP_SELF']); if ($dir_url == DIRECTORY_SEPARATOR) { $skip_path_components = 0; } else { $skip_path_components = count(explode('/', str_strip_prefix($dir_url, '/'))); } } /* Initialization */ $get_params = ''; /* Use the current url if no explicit url was given */ if (is_null($url)) { $url = Request::relative_url(); } /* We need to strip off the GET parameters */ $question_mark_pos = strpos($url, '?'); if ($question_mark_pos !== false) { $get_params = substr($url, $question_mark_pos); $url = substr($url, 0, $question_mark_pos); } /* Sanity checks */ assert('is_int($skip_path_components) && $skip_path_components >= 0'); assert('is_string($url)'); /* Force trailing slash for GET requests? */ if (Request::is_get() && $this->_getdefault('force-trailing-slash', true) && $url != '/' && !str_has_suffix($url, '/') && preg_match('/^.*\\/[^.\\/]+$/', $url)) { redirect($url . '/' . $get_params, HTTP_STATUS_MOVED_PERMANENTLY); } /* Store the url so that it can be used later */ $this->_set('url', $url); /* Split the url into smaller pieces */ $url = str_strip_prefix($url, '/'); $url = str_strip_suffix($url, '/'); $components = split('/', $url); /* Skip some components if requested, and store the cut-off part in the * 'base-url' property. */ if (count($components) < $skip_path_components) { $this->_handle_result(HTTP_STATUS_INTERNAL_SERVER_ERROR); } $base_url = sprintf('/%s/', join('/', array_slice($components, 0, $skip_path_components))); $this->_set('base-url', $base_url); $components = array_slice($components, $skip_path_components); /* Special case for top level urls */ if (count($components) == 1 && strlen($components[0]) == 0) { $components = array(); } /* Try all URL maps and see if they match the input url */ $found_map = false; $command_name = null; $parameters = array(); foreach ($this->urlmaps as $urlmap) { list($command_name, $patterns) = $urlmap; /* Check for valid parameters */ $match = $this->_match_inputs_with_patterns($components, $patterns); /* This urlmap didn't match, try next one */ if ($match === false) { continue; /* This urlmap matched! */ } else { $parameters = $match; $found_map = true; break; } } /* No explicit map found, try an implicit map */ if (!$found_map && $this->_getdefault('use-implicit-commands', true)) { $command_name = join('_', $components); $command_name = str_replace('-', '_', $command_name); /* The method must exist */ $command = 'command_' . $command_name; $found_map = method_exists($this, $command); } /* As a last resort try the default handler, if one was set. There's no * need to check the availability of the method; set_default_command() * already did that. */ if (!$found_map && $this->_isset('default-command')) { $command_name = $this->_get('default-command'); $found_map = true; } /* Sanity check: is the method available? */ $command = 'command_' . $command_name; if (!method_exists($this, $command)) { /* FIXME: it's not clear if this is desirable */ /* We found a handler name but the method doesn't exist... */ /* Trying the default handler, but remember which command * we wanted to access. */ if (!$this->_isset('default_command')) { /* We give up. */ $found_map = false; } else { $command = 'command_' . $this->_get('default-command'); } } /* If we still don't have a command, we give up. Too bad... not found */ if (!$found_map) { $this->_handle_result(HTTP_STATUS_NOT_FOUND); return false; } /* Store the command name for use by _handle_result() and possibly * pre_command(). */ $this->_set('command', $command_name); /* If this piece of code is reached, a valid command has been found. Run * the pre-command hook, call the appropriate method and handle the * result. The pre_command() method may return HTTP_STATUS_NOT_FOUND or * HTTP_STATUS_FORBIDDEN as well, so we need to handle the status * carefully. */ $status = $this->pre_command($parameters); /* The pre_command() method is not required to have a return value. If * it doesn't return anything, $status will be null at this point. If * that's the case we assume everything is alright. */ if (is_null($status)) { $status = HTTP_STATUS_OK; } if ($status == HTTP_STATUS_OK) { /* The pre_command() method succeeded. Run the actual command and * keep track of the status. */ $status = $this->{$command}($parameters); } /* Regardless of whether the actual command has been executed, the * result handler is invoked. Note: The post_command() is only invoked * if both the pre_command() and the actual command method return * HTTP_STATUS_OK (there's no danger of calling post_command() if no * real command has been executed). */ $this->_handle_result($status); }
/** * Initialize a new AnewtForm instance. * * Do not forget to call this method when you override the constructor in * a subclass, i.e. call <code>parent::__construct()</code>. */ function __construct() { /* Default values */ $id = sprintf('form-%s', str_strip_suffix(strtolower(get_class($this)), 'form')); $this->_seed(array('id' => $id, 'method' => ANEWT_FORM_METHOD_POST, 'action' => Request::relative_url(), 'description' => null, 'error' => null)); }
/** * Return a list of all defined names. * * This method includes both keys in the internal storage and the name of * values available only through getter methods. * * \return * A numeric array with all keys. * * \see Container::_keys */ function keys() { /* All internal keys... */ $result = $this->_keys(); /* ... and all keys found in getter methods */ foreach (get_class_methods($this) as $method_name) { if (str_has_prefix($method_name, 'get_')) { /* Remove 'get_' prefix, handle caching getters and normalize */ $name = substr($method_name, 4); $name = str_strip_suffix($name, '_'); $name = str_replace('_', '-', $name); $result[] = $name; } } return $result; }
/** * (Really) dispatch an URL to the correct handlers. * * This method does the actual magic, such as URL parsing, matching and * command invocation. You can optionally provide a custom URL and tell the * dispatcher that some parts of the URL should be skipped when handling * this request. * * \param $url * \param $prefix * \see AnewtURLDispatcher::dispatch */ private function real_dispatch($url = null, $prefix = null) { /* Use the current URL if no explicit url was given */ if (is_null($url)) { $url = AnewtRequest::relative_url(); } /* Figure out the right base location if no prefix was given. If the URL * starts with the PHP script name, we assume no htaccess file has been * setup to beautify the website URLs. In this case the relevant parts * of the URL are added after the PHP script name. Example URL of such * a setup is http://.../dispatch.php/a/b/c/. Otherwise, it is quite * likely a htaccess file is used to point all requests to a script that * invokes the dispatcher. We assume this script is placed in the * toplevel directory, so we use that directory as the prefix. */ if (is_null($prefix)) { if (str_has_prefix($url, $_SERVER['SCRIPT_NAME'])) { $prefix = $_SERVER['SCRIPT_NAME']; } else { $prefix = dirname($_SERVER['SCRIPT_NAME']); } } assert('is_string($url)'); assert('is_string($prefix)'); /* Strip off the GET parameters from the URL */ $get_params = ''; $question_mark_pos = strpos($url, '?'); if ($question_mark_pos !== false) { $get_params = substr($url, $question_mark_pos); $url = substr($url, 0, $question_mark_pos); } /* Redirect GET requests when trailing slash is required but missing */ if (!str_has_suffix($url, '/') && $this->force_trailing_slash && AnewtRequest::is_get() && !preg_match('#^.*\\.[^\\/]*$#', $url)) { redirect(sprintf('%s/%s', $url, $get_params), HTTP_STATUS_MOVED_PERMANENTLY); } /* Strip off prefix and slashes */ $this->request_url_full = $url; $url = str_strip_prefix($url, $prefix); $url = str_strip_prefix($url, '/'); $url = str_strip_suffix($url, '/'); $this->request_url = $url; /* Try to find a matching route and extract the parameters */ $found_route = false; $command = null; $parameters = array(); $url_parts = strlen($url) > 0 ? explode('/', $url) : array(); foreach ($this->routes as $route) { $route_type = array_shift($route); $route_command = array_shift($route); $route_parameters = array(); /* Type I: Routes using regular expression */ if ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_REGEX) { list($pattern) = $route; /* Try both with and without trailing slash */ if (preg_match($pattern, $url, $route_parameters) || preg_match($pattern, sprintf('%s/', $url), $route_parameters)) { /* We don't care about $parameters[0] (it contains the full match) */ array_shift($route_parameters); $route_parameters = array_map('urldecode', $route_parameters); $command = $route_command; $parameters = $route_parameters; $found_route = true; break; } } elseif ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_URL_PARTS) { list($route_url, $additional_constraints) = $route; /* Route URL can be a string or an array */ if (is_string($route_url)) { $route_url = str_strip_prefix($route_url, '/'); $route_url = str_strip_suffix($route_url, '/'); $route_url_parts = strlen($route_url) > 0 ? explode('/', $route_url) : array(); } elseif (is_numeric_array($route_url)) { $route_url_parts = $route_url; } else { throw new AnewtException('Invalid url route: %s', $route_url); } /* Match the URL parts against the route URL parts */ if (count($url_parts) != count($route_url_parts)) { continue; } $constraints = array_merge($this->url_part_constraints, $additional_constraints); for ($i = 0; $i < count($url_parts); $i++) { /* If the URL starts with a ':' character it is * a parameter... */ if ($route_url_parts[$i][0] === ':') { $parameter_name = substr($route_url_parts[$i], 1); $parameter_value = $url_parts[$i]; /* If there is a constraint for this parameter, the * value must match the constraint. If not, this route * cannot be used. */ if (array_key_exists($parameter_name, $constraints)) { $pattern = $constraints[$parameter_name]; if (!preg_match($pattern, $parameter_value)) { continue 2; } } $route_parameters[$parameter_name] = urldecode($parameter_value); } elseif ($url_parts[$i] !== $route_url_parts[$i]) { continue 2; } } /* If this code is reached, we found a matching route with all * the constraints on the URL parts satisfied. */ $command = $route_command; $parameters = $route_parameters; $found_route = true; break; } else { assert('false; // not reached'); } } /* If no route matches, try an automatic route. Only the first URL part * is considered for this. */ if (!$found_route && $this->use_automatic_commands) { $url_parts = explode('/', $url, 2); if ($url_parts) { $command = array($this, sprintf('command_%s', $url_parts[0])); list($found_route, $error_message_to_ignore) = $this->is_valid_command($command); } } /* As a last resort try the default handler, if one was set. */ $default_command = $this->default_command; if (!$found_route && !is_null($default_command)) { $command = $default_command; $command = $this->validate_command($command); $found_route = true; } /* If we still don't have a command, we give up. Too bad... not found */ if (!$found_route) { throw new AnewtHTTPException(HTTP_STATUS_NOT_FOUND); } /* Check the command for validity. In most cases we already know the * command exists since that is already checked in the add_route_*() * methods or in the code above, except for lazily loaded commands, so * we try to load them and check for validity afterwards. */ if (is_array($command) && is_string($command[0])) { $this->include_command_class($command[0]); $command = $this->validate_command($command); } /* Finally... run the command and the pre and post command hooks. */ $this->pre_command($parameters); call_user_func($command, $parameters); $this->post_command($parameters); }
/** * Build a URL from the passed parameters. * * You can provide a single path string, or an array of strings, in which * case each of the items in \c $path is a path component. The path * components will be concatenated, separated by slashes. * * All slashes are normalized. If the first path component has a leading * slash, the resulting string will also have a leading slash and if it * doesn't, the resulting string won't have one either. The same goes for * the trailing slash: if the last path component ends with a slash, the * resulting string will have one as well. * * If \c $parameters is passed, a HTTP query string will be appended to the * url using the this associatve array. * * Example: * * <code>$url = AnewtURL::join(array('/path/to', $file), array('foo' => 'bar'));</code> * * \param $path * Single string or array of strings (each item is a path component of the url) * * \param $parameters * Associative array used to build a query string (optional) * * \return * The resulting URL path * * \see * AnewtURL::parse */ static function build($path, $parameters = null) { /* Input sanitizing */ if (is_string($path)) { $path_components = array($path); } else { $path_components = $path; } if (is_null($parameters)) { $parameters = array(); } assert('is_numeric_array($path_components);'); assert('is_assoc_array($parameters);'); /* Remove empty path components */ $path_components = str_all_non_white($path_components); /* Leading and trailing slashes */ if ($path_components) { /* Path is not empty */ $use_leading_slash = str_has_prefix($path_components[0], '/'); $use_trailing_slash = str_has_suffix($path_components[count($path_components) - 1], '/'); } else { /* Path is empty */ $use_leading_slash = false; $use_trailing_slash = false; } /* Loop over url parts and clean up */ $first_part_seen = false; $path_components_clean = array(); while ($path_components) { $part = array_shift($path_components); assert('is_string($part)'); $part = str_strip_prefix($part, '/'); $part = str_strip_suffix($part, '/'); if (!strlen($part)) { continue; } /* Use url encoding, but the first path component may be something * like "http://...", which should not be url encoded. */ if (!$first_part_seen && str_contains($part, '://')) { $first_part_seen = true; $part_encoded = $part; } else { $part_encoded = urlencode($part); /* Url decoding also escapes slashes and some other characters * we want to keep, since escaping those would disallow passing * path components like "/path/to/file". A slash cannot be used * in a filename anyway, so we special-case some characters. */ $part_encoded = str_replace(array('%2F', '%7E'), array('/', '~'), $part_encoded); } $path_components_clean[] = $part_encoded; } /* Build url by joining all cleaned parts, and adding a leading and * trailing slash, if appropriate. */ $url = join('/', $path_components_clean); if (strlen($url)) { /* Path is not empty */ if ($use_leading_slash) { $url = sprintf('/%s', $url); } if ($use_trailing_slash) { $url = sprintf('%s/', $url); } } elseif ($use_leading_slash || $use_trailing_slash) { /* Path is empty, a slash is required */ $url = '/'; } /* Query parameters */ assert('is_assoc_array($parameters)'); $parameters_escaped = array(); foreach ($parameters as $name => $value) { if (is_null($value)) { $parameters_escaped[] = urlencode($name); } else { assert('is_string($value);'); $parameters_escaped[] = sprintf('%s=%s', urlencode($name), urlencode($value)); } } if ($parameters_escaped) { $url = sprintf('%s?%s', $url, implode('&', $parameters_escaped)); } return $url; }
/** * Outputs the iCalendar to a browser. * * \param $exit_after_flush * Boolean value indicating whether to stop execution after flushing to * the client (default false). If true, make sure the call to flush() is * the last call in your code, because statements below the call to * flush() will never be executed. */ function flush($exit_after_flush = false) { /* MIME type */ header('Content-Type: text/calendar'); /* Easier debugging (without a browser "save as" dialog) */ if ($this->getdefault('debug', false)) { header('Content-Type: text/plain'); } /* A filename is optional, but it is recommended to set one for better * client compatibility (some platforms ignore the MIME type information * and use the file name (extension) to determine what actions to * perform. */ if ($this->is_set('filename')) { $filename = $this->get('filename'); $filename = str_strip_suffix($filename, '.ics'); header(sprintf('Content-Disposition: inline; filename=%s.ics', $filename)); } echo $this->to_ical(); assert('is_bool($exit_after_flush)'); if ($exit_after_flush) { exit(0); } }