KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Route.php 8.3KB

9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. /**
  3. * Class: Route
  4. * Holds information for URLs, redirecting, etc.
  5. */
  6. class Route {
  7. # String: $action
  8. # The current action.
  9. public $action = "";
  10. # Array: $try
  11. # An array of (string) actions to try until one doesn't return false.
  12. public $try = array();
  13. # Boolean: $ajax
  14. # Shortcut to the AJAX constant (useful for Twig).
  15. public $ajax = AJAX;
  16. # Boolean: $success
  17. # Did <Route.init> call a successful route?
  18. public $success = false;
  19. # Variable: $controller
  20. # The Route's Controller.
  21. public $controller;
  22. /**
  23. * Function: __construct
  24. * Parse the URL and to determine what to do.
  25. *
  26. * Parameters:
  27. * $controller - The controller to use.
  28. */
  29. private function __construct($controller) {
  30. $this->controller = $controller;
  31. $config = Config::current();
  32. if (substr_count($_SERVER['REQUEST_URI'], "..") > 0 )
  33. exit("GTFO.");
  34. elseif (isset($_GET['action']) and preg_match("/[^(\w+)]/", $_GET['action']))
  35. exit("Nope!");
  36. $this->action =& $_GET['action'];
  37. if (isset($_GET['feed']))
  38. $this->feed = true;
  39. # Parse the current URL and extract information.
  40. $parse = parse_url($config->url);
  41. fallback($parse["path"], "/");
  42. if (isset($controller->base))
  43. $parse["path"] = trim($parse["path"], "/")."/".trim($controller->base, "/")."/";
  44. $this->safe_path = str_replace("/", "\\/", $parse["path"]);
  45. $this->request = $parse["path"] == "/" ?
  46. $_SERVER['REQUEST_URI'] :
  47. preg_replace("/{$this->safe_path}?/", "", $_SERVER['REQUEST_URI'], 1) ;
  48. $this->arg = array_map("urldecode", explode("/", trim($this->request, "/")));
  49. if (substr_count($this->arg[0], "?") > 0 and !preg_match("/\?\w+/", $this->arg[0]))
  50. exit("No-Go!");
  51. if (method_exists($controller, "parse"))
  52. $controller->parse($this);
  53. Trigger::current()->call("parse_url", $this);
  54. $this->try[] = isset($this->action) ?
  55. oneof($this->action, "index") :
  56. (!substr_count($this->arg[0], "?") ?
  57. oneof(@$this->arg[0], "index") :
  58. "index") ;
  59. # Guess the action initially.
  60. # This is only required because of the view_site permission;
  61. # it has to know if they're viewing /login, in which case
  62. # it should allow the page to display.
  63. fallback($this->action, end($this->try));
  64. }
  65. /**
  66. * Function: init
  67. * Attempt Controller actions until one of them doesn't return false.
  68. *
  69. * This will also call the @[controllername]_xxxxx@ and @route_xxxxx@ triggers.
  70. */
  71. public function init() {
  72. $trigger = Trigger::current();
  73. $trigger->call("route_init", $this);
  74. $try = $this->try;
  75. if (isset($this->action))
  76. array_unshift($try, $this->action);
  77. $count = 0;
  78. foreach ($try as $key => $val) {
  79. if (is_numeric($key))
  80. list($method, $args) = array($val, array());
  81. else
  82. list($method, $args) = array($key, $val);
  83. $this->action = $method;
  84. $name = strtolower(str_replace("Controller", "", get_class($this->controller)));
  85. if ($trigger->exists($name."_".$method) or $trigger->exists("route_".$method))
  86. $call = $trigger->call(array($name."_".$method, "route_".$method), $this->controller);
  87. else
  88. $call = false;
  89. if ($call !== true and method_exists($this->controller, $method))
  90. $response = call_user_func_array(array($this->controller, $method), $args);
  91. else
  92. $response = false;
  93. if ($response !== false or $call !== false) {
  94. $this->success = true;
  95. break;
  96. }
  97. if (++$count == count($try) and isset($this->controller->fallback) and method_exists($this->controller, "display"))
  98. call_user_func_array(array($this->controller, "display"), $this->controller->fallback);
  99. }
  100. if ($this->action != "login" and $this->success)
  101. $_SESSION['redirect_to'] = self_url();
  102. $trigger->call("route_done", $this);
  103. return true;
  104. }
  105. /**
  106. * Function: url
  107. * Attempts to change the specified clean URL to a dirty URL if clean URLs is disabled.
  108. * Use this for linking to things. The applicable URL conversions are passed through the
  109. * parse_urls trigger.
  110. *
  111. * Parameters:
  112. * $url - The clean URL.
  113. * $use_chyrp_url - Use @Config.chyrp_url@ instead of @Config.url@, when the @$url@ begins with "/"?
  114. *
  115. * Returns:
  116. * A clean or dirty URL, depending on @Config.clean_urls@.
  117. */
  118. public function url($url, $controller = null) {
  119. $config = Config::current();
  120. if ($url[0] == "/")
  121. return (ADMIN ?
  122. $config->chyrp_url.$url :
  123. $config->url.$url);
  124. else
  125. $url = substr($url, -1) == "/" ? $url : $url."/" ;
  126. fallback($controller, $this->controller);
  127. $base = !empty($controller->base) ? $config->url."/".$controller->base : $config->url ;
  128. if ($config->clean_urls) { # If their post URL doesn't have a trailing slash, remove it from these as well.
  129. if (substr($url, 0, 5) == "page/") # Different URL for viewing a page
  130. $url = substr($url, 5);
  131. return (substr($config->post_url, -1) == "/" or $url == "search/") ?
  132. $base."/".$url :
  133. $base."/".rtrim($url, "/") ;
  134. }
  135. $urls = fallback($controller->urls, array());
  136. Trigger::current()->filter($urls, "parse_urls");
  137. foreach (array_diff_assoc($urls, $controller->urls) as $key => $value)
  138. $urls[substr($key, 0, -1).preg_quote("feed/", $key[0]).$key[0]] = "/".$value."&amp;feed";
  139. $urls["|/([^/]+)/$|"] = "/?action=$1";
  140. return $base.fix(preg_replace(array_keys($urls), array_values($urls), "/".$url, 1));
  141. }
  142. /**
  143. * Function: add
  144. * Adds a route to Chyrp. Only needed for actions that have more than one parameter.
  145. * For example, for /tags/ you won't need to do this, but you will for /tag/tag-name/.
  146. *
  147. * Parameters:
  148. * $path - The path to add. Wrap variables with parentheses, e.g. "tag/(name)/".
  149. * $action - The action the path points to.
  150. *
  151. * See Also:
  152. * <remove>
  153. */
  154. public function add($path, $action) {
  155. $config = Config::current();
  156. $new_routes = $config->routes;
  157. $new_routes[$path] = $action;
  158. $config->set("routes", $new_routes);
  159. }
  160. /**
  161. * Function: remove
  162. * Removes a route added by <add>.
  163. *
  164. * Parameters:
  165. * $path - The path to remove.
  166. *
  167. * See Also:
  168. * <add>
  169. */
  170. public function remove($path) {
  171. $config = Config::current();
  172. unset($config->routes[$path]);
  173. $config->set("routes", $config->routes);
  174. }
  175. /**
  176. * Function: current
  177. * Returns a singleton reference to the current class.
  178. */
  179. public static function & current($controller = null) {
  180. static $instance = null;
  181. if (!isset($controller) and empty($instance))
  182. error(__("Error"), __("Route was initiated without a Controller."), debug_backtrace());
  183. return $instance = (empty($instance)) ? new self($controller) : $instance ;
  184. }
  185. }