1 <?php
defined('SYSPATH') or die('No direct script access.');
3 * Kohana user guide and api browser.
5 * @package Kohana/Userguide
8 * @copyright (c) 2008-2013 Kohana Team
9 * @license http://kohanaframework.org/license
11 abstract class Kohana_Controller_Userguide
extends Controller_Template
{
13 public $template = 'userguide/template';
20 public function before()
24 if ($this->request
->action() === 'media')
26 // Do not template media files
27 $this->auto_render
= FALSE;
31 // Grab the necessary routes
32 $this->media
= Route
::get('docs/media');
33 $this->guide
= Route
::get('docs/guide');
35 // Set the base URL for links and images
36 Kodoc_Markdown
::$base_url = URL
::site($this->guide
->uri()).'/';
37 Kodoc_Markdown
::$image_url = URL
::site($this->media
->uri()).'/';
40 // Default show_comments to config value
41 $this->template
->show_comments
= Kohana
::$config->load('userguide.show_comments');
44 // List all modules that have userguides
45 public function index()
47 $this->template
->title
= "Userguide";
48 $this->template
->breadcrumb
= array('User Guide');
49 $this->template
->content
= View
::factory('userguide/index', array('modules' => $this->_modules()));
50 $this->template
->menu
= View
::factory('userguide/menu', array('modules' => $this->_modules()));
52 // Don't show disqus on the index page
53 $this->template
->show_comments
= FALSE;
56 // Display an error if a page isn't found
57 public function error($message)
59 $this->response
->status(404);
60 $this->template
->title
= "Userguide - Error";
61 $this->template
->content
= View
::factory('userguide/error',array('message' => $message));
63 // Don't show disqus on error pages
64 $this->template
->show_comments
= FALSE;
66 // If we are in a module and that module has a menu, show that
67 if ($module = $this->request
->param('module') AND $menu = $this->file($module.'/menu') AND Kohana
::$config->load('userguide.modules.'.$module.'.enabled'))
69 // Namespace the markdown parser
70 Kodoc_Markdown
::$base_url = URL
::site($this->guide
->uri()).'/'.$module.'/';
71 Kodoc_Markdown
::$image_url = URL
::site($this->media
->uri()).'/'.$module.'/';
73 $this->template
->menu
= Kodoc_Markdown
::markdown($this->_get_all_menu_markdown());
74 $this->template
->breadcrumb
= array(
75 $this->guide
->uri() => 'User Guide',
76 $this->guide
->uri(array('module' => $module)) => Kohana
::$config->load('userguide.modules.'.$module.'.name'),
80 // If we are in the api browser, show the menu and show the api browser in the breadcrumbs
81 elseif (Route
::name($this->request
->route()) == 'docs/api')
83 $this->template
->menu
= Kodoc
::menu();
85 // Bind the breadcrumb
86 $this->template
->breadcrumb
= array(
87 $this->guide
->uri(array('page' => NULL)) => 'User Guide',
88 $this->request
->route()->uri() => 'API Browser',
92 // Otherwise, show the userguide module menu on the side
95 $this->template
->menu
= View
::factory('userguide/menu',array('modules' => $this->_modules()));
96 $this->template
->breadcrumb
= array($this->request
->route()->uri() => 'User Guide','Error');
100 public function action_docs()
102 $module = $this->request
->param('module');
103 $page = $this->request
->param('page');
105 // Trim trailing slash
106 $page = rtrim($page, '/');
108 // If no module provided in the url, show the user guide index page, which lists the modules.
111 return $this->index();
114 // If this module's userguide pages are disabled, show the error page
115 if ( ! Kohana
::$config->load('userguide.modules.'.$module.'.enabled'))
117 return $this->error('That module doesn\'t exist, or has userguide pages disabled.');
120 // Prevent "guide/module" and "guide/module/index" from having duplicate content
121 if ($page == 'index')
123 return $this->error('Userguide page not found');
126 // If a module is set, but no page was provided in the url, show the index page
132 // Find the markdown file for this page
133 $file = $this->file($module.'/'.$page);
135 // If it's not found, show the error page
138 return $this->error('Userguide page not found');
141 // Namespace the markdown parser
142 Kodoc_Markdown
::$base_url = URL
::site($this->guide
->uri()).'/'.$module.'/';
143 Kodoc_Markdown
::$image_url = URL
::site($this->media
->uri()).'/'.$module.'/';
145 // Set the page title
146 $this->template
->title
= ($page == 'index')
147 ? Kohana
::$config->load('userguide.modules.'.$module.'.name')
148 : $this->title($page);
150 // Parse the page contents into the template
151 Kodoc_Markdown
::$show_toc = TRUE;
152 $this->template
->content
= Kodoc_Markdown
::markdown(file_get_contents($file));
153 Kodoc_Markdown
::$show_toc = FALSE;
155 // Attach this module's menu to the template
156 $this->template
->menu
= Kodoc_Markdown
::markdown($this->_get_all_menu_markdown());
158 // Bind the breadcrumb
159 $this->template
->bind('breadcrumb', $breadcrumb);
161 // Bind the copyright
162 $this->template
->copyright
= Kohana
::$config->load('userguide.modules.'.$module.'.copyright');
164 // Add the breadcrumb trail
165 $breadcrumb = array();
166 $breadcrumb[$this->guide
->uri()] = 'User Guide';
167 $breadcrumb[$this->guide
->uri(array('module' => $module))] = Kohana
::$config->load('userguide.modules.'.$module.'.name');
169 // TODO try and get parent category names (from menu). Regex magic or javascript dom stuff perhaps?
171 // Only add the current page title to breadcrumbs if it isn't the index, otherwise we get repeats.
172 if ($page != 'index')
174 $breadcrumb[] = $this->template
->title
;
178 public function action_api()
180 // Enable the missing class autoloader. If a class cannot be found a
181 // fake class will be created that extends Kodoc_Missing
182 spl_autoload_register(array('Kodoc_Missing', 'create_class'));
184 // Get the class from the request
185 $class = $this->request
->param('class');
187 // If no class was passed to the url, display the API index page
190 $this->template
->title
= 'Table of Contents';
192 $this->template
->content
= View
::factory('userguide/api/toc')
193 ->set('classes', Kodoc
::class_methods())
194 ->set('route', $this->request
->route());
198 // Create the Kodoc_Class version of this class.
199 $_class = Kodoc_Class
::factory($class);
201 // If the class requested and the actual class name are different
202 // (different case, orm vs ORM, auth vs Auth) redirect
203 if ($_class->class->name
!= $class)
205 $this->redirect($this->request
->route()->uri(array('class'=>$_class->class->name
)));
208 // If this classes immediate parent is Kodoc_Missing, then it should 404
209 if ($_class->class->getParentClass() AND $_class->class->getParentClass()->name
== 'Kodoc_Missing')
210 return $this->error('That class was not found. Check your url and make sure that the module with that class is enabled.');
212 // If this classes package has been disabled via the config, 404
213 if ( ! Kodoc
::show_class($_class))
214 return $this->error('That class is in package that is hidden. Check the <code>api_packages</code> config setting.');
216 // Everything is fine, display the class.
217 $this->template
->title
= $class;
219 $this->template
->content
= View
::factory('userguide/api/class')
220 ->set('doc', $_class)
221 ->set('route', $this->request
->route());
224 // Attach the menu to the template
225 $this->template
->menu
= Kodoc
::menu();
227 // Bind the breadcrumb
228 $this->template
->bind('breadcrumb', $breadcrumb);
230 // Add the breadcrumb
231 $breadcrumb = array();
232 $breadcrumb[$this->guide
->uri(array('page' => NULL))] = 'User Guide';
233 $breadcrumb[$this->request
->route()->uri()] = 'API Browser';
234 $breadcrumb[] = $this->template
->title
;
237 public function action_media()
239 // Get the file path from the request
240 $file = $this->request
->param('file');
242 // Find the file extension
243 $ext = pathinfo($file, PATHINFO_EXTENSION
);
245 // Remove the extension from the filename
246 $file = substr($file, 0, -(strlen($ext) +
1));
248 if ($file = Kohana
::find_file('media/guide', $file, $ext))
250 // Check if the browser sent an "if-none-match: <etag>" header, and tell if the file hasn't changed
251 $this->check_cache(sha1($this->request
->uri()).filemtime($file));
253 // Send the file content as the response
254 $this->response
->body(file_get_contents($file));
256 // Set the proper headers to allow caching
257 $this->response
->headers('content-type', File
::mime_by_ext($ext));
258 $this->response
->headers('last-modified', date('r', filemtime($file)));
262 // Return a 404 status
263 $this->response
->status(404);
267 public function after()
269 if ($this->auto_render
)
271 // Get the media route
272 $media = Route
::get('docs/media');
275 $this->template
->styles
= array(
276 $media->uri(array('file' => 'css/print.css')) => 'print',
277 $media->uri(array('file' => 'css/screen.css')) => 'screen',
278 $media->uri(array('file' => 'css/kodoc.css')) => 'screen',
279 $media->uri(array('file' => 'css/shCore.css')) => 'screen',
280 $media->uri(array('file' => 'css/shThemeKodoc.css')) => 'screen',
284 $this->template
->scripts
= array(
285 $media->uri(array('file' => 'js/jquery.min.js')),
286 $media->uri(array('file' => 'js/jquery.cookie.js')),
287 $media->uri(array('file' => 'js/kodoc.js')),
288 // Syntax Highlighter
289 $media->uri(array('file' => 'js/shCore.js')),
290 $media->uri(array('file' => 'js/shBrushPhp.js')),
294 $this->template
->translations
= Kohana
::message('userguide', 'translations');
297 return parent
::after();
301 * Locates the appropriate markdown file for a given guide page. Page URLS
302 * can be specified in one of three forms:
305 * * userguide/adding.md
306 * * userguide/adding.markdown
308 * In every case, the userguide will search the cascading file system paths
309 * for the file guide/userguide/adding.md.
311 * @param string $page The relative URL of the guide page
314 public function file($page)
317 // Strip optional .md or .markdown suffix from the passed filename
318 $info = pathinfo($page);
319 if (isset($info['extension'])
320 AND (($info['extension'] === 'md') OR ($info['extension'] === 'markdown')))
322 $page = $info['dirname'].DIRECTORY_SEPARATOR
.$info['filename'];
324 return Kohana
::find_file('guide', $page, 'md');
327 public function section($page)
329 $markdown = $this->_get_all_menu_markdown();
331 if (preg_match('~\*{2}(.+?)\*{2}[^*]+\[[^\]]+\]\('.preg_quote($page).'\)~mu', $markdown, $matches))
339 public function title($page)
341 $markdown = $this->_get_all_menu_markdown();
343 if (preg_match('~\[([^\]]+)\]\('.preg_quote($page).'\)~mu', $markdown, $matches))
345 // Found a title for this link
352 protected function _get_all_menu_markdown()
354 // Only do this once per request...
355 static $markdown = '';
357 if (empty($markdown))
360 $file = $this->file($this->request
->param('module').'/menu');
362 if ($file AND $text = file_get_contents($file))
364 // Add spans around non-link categories. This is a terrible hack.
365 $text = preg_replace('/^(\s*[\-\*\+]\s*)([^\[\]]+)$/m','$1<span>$2</span>',$text);
374 // Get the list of modules from the config, and reverses it so it displays in the order the modules are added, but move Kohana to the top.
375 protected function _modules()
377 $modules = array_reverse(Kohana
::$config->load('userguide.modules'));
379 if (isset($modules['kohana']))
381 $kohana = $modules['kohana'];
382 unset($modules['kohana']);
383 $modules = array_merge(array('kohana' => $kohana), $modules);
386 // Remove modules that have been disabled via config
387 foreach ($modules as $key => $value)
389 if ( ! Kohana
::$config->load('userguide.modules.'.$key.'.enabled'))
391 unset($modules[$key]);