'([0-9]{4})', '(month)' => '([0-9]{1,2})', '(day)' => '([0-9]{1,2})', '(hour)' => '([0-9]{1,2})', '(minute)' => '([0-9]{1,2})', '(second)' => '([0-9]{1,2})', '(id)' => '([0-9]+)', '(author)' => '([^\/]+)', '(clean)' => '([^\/]+)', '(url)' => '([^\/]+)', '(feather)' => '([^\/]+)', '(feathers)' => '([^\/]+)'); # Function: $urls # An array of clean URL => dirty URL translations. public $urls = array('/\/id\/([0-9]+)\//' => '/?action=view&id=$1', '/\/page\/(([^\/]+)\/)+/' => '/?action=page&url=$2', '/\/search\//' => '/?action=search', '/\/search\/([^\/]+)\//' => '/?action=search&query=$1', '/\/archive\/([0-9]{4})\/([0-9]{2})\//' => '/?action=archive&year=$1&month=$2', '/\/archive\/([0-9]{4})\/([0-9]{2})\/([0-9]{2})\//' => '/?action=archive&year=$1&month=$2&day=$3', '/\/theme_preview\/([^\/]+)\//' => '/?action=theme_preview&theme=$1', '/\/([^\/]+)\/feed\/([^\/]+)\//' => '/?action=$1&feed&title=$2', '/\/([^\/]+)\/feed\//' => '/?action=$1&feed'); # String: $action # The current action. public $action = ""; # Boolean: $ajax # Shortcut to the AJAX constant (useful for Twig). public $ajax = AJAX; # Array: $post_url_attrs # Contains an associative array of URL key to value arguments if we're viewing a post. public $post_url_attrs = array(); /** * Function: __construct * Filters the key => val code so that modules may extend it. */ private function __construct() {} /** * Function: url * Attempts to change the specified clean URL to a dirty URL if clean URLs is disabled. * Use this for linking to things. The applicable URL conversions are passed through the * parse_urls trigger. * * Parameters: * $url - The clean URL. * * Returns: * Clean URL - if $config->clean_urls is set to *true*. * Dirty URL - if $config->clean_urls is set to *false*. */ public function url($url, $use_chyrp_url = false) { $config = Config::current(); if ($url[0] == "/") return (ADMIN or $use_chyrp_url) ? Config::current()->chyrp_url.$url : Config::current()->url.$url ; if ($config->clean_urls) { # If their post URL doesn't have a trailing slash, remove it from these as well. if (substr($url, 0, 5) == "page/") # Different URL for viewing a page $url = substr($url, 5); return (substr($config->post_url, -1) == "/" or $url == "search/") ? $config->url."/".$url : $config->url."/".rtrim($url, "/") ; } $urls = $this->urls; Trigger::current()->filter($urls, "parse_urls"); foreach (array_diff_assoc($urls, $this->urls) as $key => $value) $urls[substr($key, 0, -1)."feed\//"] = "/".$value."&feed"; $urls["/\/(.*?)\/$/"] = "/?action=$1"; return $config->url.preg_replace( array_keys($urls), array_values($urls), "/".$url, 1); } /** * Function: key_regexp * Converts the values in $config->post_url to regular expressions. * * Parameters: * $key - Input URL with the keys from $code>. * * Returns: * $key values replaced with their regular expressions from $code>. */ private function key_regexp($key) { Trigger::current()->filter($this->code, "url_code"); return str_replace(array_keys($this->code), array_values($this->code), str_replace("/", "\\/", $key)); } /** * Function: determine_action * This meaty function determines what exactly to do with the URL. */ public function determine_action() { global $page; $config = Config::current(); $this->action =& $_GET['action']; # Parse the current URL and extract information. $parse = parse_url($config->url); fallback($parse["path"]); $this->safe_path = str_replace("/", "\\/", $parse["path"]); $this->request = preg_replace("/".$this->safe_path."/", "", $_SERVER['REQUEST_URI'], 1); $this->arg = explode("/", trim($this->request, "/")); if (empty($this->arg[0])) # If they're just at /, don't bother with all this. return $this->action = "index"; # Feed if (preg_match("/\/feed\/?$/", $this->request)) { $_GET['feed'] = "true"; if ($this->arg[0] == "feed") # Don't set $this->action to "feed" (bottom of this function). return $this->action = "index"; } # Feed with a title parameter if (preg_match("/\/feed\/([^\/]+)\/?$/", $this->request, $title)) { $_GET['feed'] = "true"; $_GET['title'] = $title[1]; if ($this->arg[0] == "feed") # Don't set $this->action to "feed" (bottom of this function). return $this->action = "index"; } # Viewing a post by its ID if ($this->arg[0] == "id") { $_GET['id'] = $this->arg[1]; return $this->action = "id"; } # Paginator if (preg_match_all("/\/((([^_\/]+)_)?page)\/([0-9]+)/", $this->request, $page_matches)) { foreach ($page_matches[1] as $key => $page_var) { $index = array_search($page_var, $this->arg); $_GET[$page_var] = $this->arg[$index + 1]; } if ($index == 0) # Don't set $this->action to "page" (bottom of this function). return $this->action = (isset($config->routes["/"])) ? $config->routes["/"] : "index" ; } # Archive if ($this->arg[0] == "archive") { # Make sure they're numeric; there might be a /page/ in there. if (isset($this->arg[1]) and is_numeric($this->arg[1])) $_GET['year'] = $this->arg[1]; if (isset($this->arg[2]) and is_numeric($this->arg[2])) $_GET['month'] = $this->arg[2]; if (isset($this->arg[3]) and is_numeric($this->arg[3])) $_GET['day'] = $this->arg[3]; return $this->action = "archive"; } # Searching if ($this->arg[0] == "search") { if (isset($this->arg[1]) and strpos($this->request, "?action=search&query=")) redirect(str_replace("?action=search&query=", "", $this->request)); if (isset($this->arg[1])) $_GET['query'] = $this->arg[1]; return $this->action = "search"; } # Theme Previewing if ($this->arg[0] == "theme_preview" and !empty($this->arg[1])) { $_GET['theme'] = $this->arg[1]; return $this->action = "theme_preview"; } # Bookmarklet if ($this->arg[0] == "bookmarklet") { $_GET['status'] = $this->arg[1]; return $this->action = "bookmarklet"; } } public function check_viewing_page($i_have_the_power = false) { global $page; $config = Config::current(); if (ADMIN or JAVASCRIPT or AJAX or XML_RPC or !$config->clean_urls or !empty($this->action)) return; if (count($this->arg) == 1 and method_exists(MainController::current(), $this->arg[0])) return $this->action = $this->arg[0]; $valids = Page::find(array("where" => "url IN ('".implode("', '", $this->arg)."')")); if (count($valids) == count($this->arg)) { foreach ($valids as $page) if ($page->url == end($this->arg)) return list($_GET['url'], $this->action) = array($page->url, "page"); } elseif ($i_have_the_power) return $this->action = fallback($this->arg[0], "index"); } public function check_viewing_post($i_have_the_power = false) { $config = Config::current(); if (ADMIN or JAVASCRIPT or AJAX or XML_RPC or !$config->clean_urls or !empty($this->action)) return; if (count($this->arg) == 1 and method_exists(MainController::current(), $this->arg[0])) return $this->action = $this->arg[0]; $post_url = $config->post_url; $request = $this->request; foreach (explode("/", $post_url) as $path) foreach (preg_split("/\(([^\)]+)\)/", $path) as $leftover) { $request = preg_replace("/".preg_quote($leftover)."/", "", $request, 1); $post_url = preg_replace("/".preg_quote($leftover)."/", "", $post_url, 1); } $args = explode("/", trim($request, "/")); $post_url = $this->key_regexp(rtrim($post_url, "/")); preg_match_all("/\(([^\/]+)\)/", $config->post_url, $parameters); if (preg_match("/".$post_url."/", rtrim($request, "/"), $matches)) { array_shift($matches); foreach ($parameters[0] as $index => $parameter) if ($parameter[0] == "(") $this->post_url_attrs[rtrim(ltrim($parameter, "("), ")")] = urldecode($args[$index]); $check = Post::from_url($this->post_url_attrs); if (!$check->no_results) return $this->action = "view"; } if ($i_have_the_power) return $this->action = fallback($this->arg[0], "index"); } public function check_custom_routes() { $config = Config::current(); # Custom pages added by Modules, Feathers, Themes, etc. foreach ($config->routes as $route => $action) { if (is_numeric($action)) $action = $this->arg[0]; preg_match_all("/\(([^\)]+)\)/", $route, $matches); if ($route != "/") $route = trim($route, "/"); $escape = preg_quote($route, "/"); $to_regexp = preg_replace("/\\\\\(([^\)]+)\\\\\)/", "([^\/]+)", $escape); if ($route == "/") $to_regexp = "\$"; if (preg_match("/^\/{$to_regexp}/", $this->request, $url_matches)) { array_shift($url_matches); if (isset($matches[1])) foreach ($matches[1] as $index => $parameter) $_GET[$parameter] = $url_matches[$index]; $params = explode(";", $action); $action = $params[0]; array_shift($params); foreach ($params as $param) { $split = explode("=", $param); $_GET[$split[0]] = $split[1]; } $its_fine = true; Trigger::current()->filter($its_fine, "check_route_".$action, $action); if ($its_fine) return $this->action = $action; } } } /** * Function: add * Adds a route to Chyrp. Only needed for actions that have more than one parameter. * For example, for /tags/ you won't need to do this, but you will for /tag/tag-name/. * * Parameters: * $path - The path to add. Wrap variables with parentheses, e.g. "tag/(name)/". * $action - The action the path points to. * * See Also: * */ public function add($path, $action) { $config = Config::current(); $new_routes = $config->routes; $new_routes[$path] = $action; $config->set("routes", $new_routes); } /** * Function: remove_route * Removes a route from the install's .htaccess file. * * Parameters: * $path - The path to remove. Same as . * * See Also: * */ public function remove($path) { $config = Config::current(); unset($config->routes[$path]); $config->set("routes", $config->routes); } /** * Function: current * Returns a singleton reference to the current class. */ public static function & current() { static $instance = null; return $instance = (empty($instance)) ? new self() : $instance ; } } $route = Route::current();