doc: Support loading images beneath doc/ directories

This commit is contained in:
Eric Lippmann 2016-04-01 14:32:33 +02:00
parent dc4bcdabb6
commit 38892a971f
6 changed files with 151 additions and 30 deletions

View File

@ -3,6 +3,8 @@
namespace Icinga\Module\Doc\Controllers;
use finfo;
use SplFileInfo;
use Icinga\Application\Icinga;
use Icinga\Module\Doc\DocController;
use Icinga\Module\Doc\Exception\DocException;
@ -120,6 +122,7 @@ class ModuleController extends DocController
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
$chapter,
'doc/module/chapter',
'doc/module/img',
array('moduleName' => $module)
);
} catch (DocException $e) {
@ -127,6 +130,60 @@ class ModuleController extends DocController
}
}
/**
* Deliver images
*/
public function imageAction()
{
$module = $this->params->getRequired('moduleName');
$image = $this->params->getRequired('image');
$docPath = $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc'));
$imagePath = realpath($docPath . '/' . $image);
if ($imagePath === false) {
$this->httpNotFound('%s does not exist', $image);
}
$this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout();
$imageInfo = new SplFileInfo($imagePath);
$ETag = md5($imageInfo->getMTime() . $imagePath);
$lastModified = gmdate('D, d M Y H:i:s T', $imageInfo->getMTime());
$match = false;
if (isset($_SERER['HTTP_IF_NONE_MATCH'])) {
$ifNoneMatch = explode(', ', stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
foreach ($ifNoneMatch as $tag) {
if ($tag === $ETag) {
$match = true;
break;
}
}
} elseif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$lastModifiedSince = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ($lastModifiedSince === $lastModified) {
$match = true;
}
}
header('ETag: "' . $ETag . '"');
header('Cache-Control: no-transform,public,max-age=3600');
header('Last-Modified: ' . $lastModified);
// Set additional headers for compatibility reasons (Cache-Control should have precedence) in case
// session.cache_limiter is set to no cache
header('Pragma: cache');
header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + 3600));
if ($match) {
header('HTTP/1.1 304 Not Modified');
} else {
$finfo = new finfo();
header('Content-Type: ' . $finfo->file($imagePath, FILEINFO_MIME_TYPE));
readfile($imagePath);
}
}
/**
* View a module's documentation as PDF
*

View File

@ -1,6 +1,6 @@
# <a id="module-documentation"></a> Writing Module Documentation
![Markdown](/img/doc/doc/markdown.png)
![Markdown](img/markdown.png)
Icinga Web 2 is capable of viewing your module's documentation, if the documentation is written in
[Markdown](http://en.wikipedia.org/wiki/Markdown). Please refer to
@ -50,13 +50,15 @@ This syntax is also supported in Icinga Web 2.
## <a id="images"></a> Including Images
Images must placed in the `img` directory beneath your module's `public` directory, e.g.:
Images must placed in the `doc` directory beneath your module's root directory, e.g.:
example-module/public/img/doc
/path/to/icingaweb2/modules/example-module/doc/img/example.png
Note that the `img` sub directory is not mandatory but good for organizing your directory structure.
Module images can be accessed using the following URL:
{baseURL}/img/{moduleName}/{file} e.g. icingaweb/img/example-module/doc/example.png
{baseURL}/doc/module/{moduleName}/image/{image} e.g. icingaweb2/doc/module/example-module/image/img/example.png
Markdown's image syntax is very similar to Markdown's link syntax, but prefixed with an exclamation mark, e.g.:
@ -64,4 +66,4 @@ Markdown's image syntax is very similar to Markdown's link syntax, but prefixed
URLs to images inside your Markdown documentation files must be specified without the base URL, e.g.:
![Example](/img/example-module/doc/example.png)
![Example](img/example.png)

View File

@ -20,6 +20,9 @@ class DocController extends Controller
if ($this->hasParam('chapter')) {
$this->params->set('chapter', $this->getParam('chapter'));
}
if ($this->hasParam('image')) {
$this->params->set('image', $this->getParam('image'));
}
if ($this->hasParam('moduleName')) {
$this->params->set('moduleName', $this->getParam('moduleName'));
}
@ -31,16 +34,18 @@ class DocController extends Controller
* @param string $path Path to the documentation
* @param string $chapter ID of the chapter
* @param string $url URL to replace links with
* @param string $imageUrl URL to images
* @param array $urlParams Additional URL parameters
*/
protected function renderChapter($path, $chapter, $url, array $urlParams = array())
protected function renderChapter($path, $chapter, $url, $imageUrl = null, array $urlParams = array())
{
$parser = new DocParser($path);
$section = new DocSectionRenderer($parser->getDocTree(), DocSectionRenderer::decodeUrlParam($chapter));
$this->view->section = $section
->setHighlightSearch($this->params->get('highlight-search'))
->setImageUrl($imageUrl)
->setUrl($url)
->setUrlParams($urlParams)
->setHighlightSearch($this->params->get('highlight-search'));
->setUrlParams($urlParams);
$this->view->title = $chapter;
$this->getTabs()->add('toc', array(
'active' => true,

View File

@ -13,6 +13,13 @@ use Icinga\Web\View;
*/
abstract class DocRenderer extends RecursiveIteratorIterator
{
/**
* URL to images
*
* @var string
*/
protected $imageUrl;
/**
* URL to replace links with
*
@ -34,6 +41,38 @@ abstract class DocRenderer extends RecursiveIteratorIterator
*/
protected $view;
/**
* Get the URL to images
*
* @return string
*/
public function getImageUrl()
{
return $this->imageUrl;
}
/**
* Set the URL to images
*
* @param string $imageUrl
*
* @return $this
*/
public function setImageUrl($imageUrl)
{
$this->imageUrl = (string) $imageUrl;
return $this;
}
/**
* Get the URL to replace links with
*
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set the URL to replace links with
*
@ -48,13 +87,13 @@ abstract class DocRenderer extends RecursiveIteratorIterator
}
/**
* Get the URL to replace links with
* Get additional URL parameters
*
* @return string
* @return array
*/
public function getUrl()
public function getUrlParams()
{
return $this->url;
return $this->urlParams;
}
/**
@ -71,13 +110,16 @@ abstract class DocRenderer extends RecursiveIteratorIterator
}
/**
* Get additional URL parameters
* Get the view
*
* @return array
* @return View
*/
public function getUrlParams()
public function getView()
{
return $this->urlParams;
if ($this->view === null) {
$this->view = Icinga::app()->getViewRenderer()->view;
}
return $this->view;
}
/**
@ -93,19 +135,6 @@ abstract class DocRenderer extends RecursiveIteratorIterator
return $this;
}
/**
* Get the view
*
* @return View
*/
public function getView()
{
if ($this->view === null) {
$this->view = Icinga::app()->getViewRenderer()->view;
}
return $this->view;
}
/**
* Encode an anchor identifier
*

View File

@ -190,7 +190,20 @@ class DocSectionRenderer extends DocRenderer
$xpath = new DOMXPath($doc);
$img = $xpath->query('//img[1]')->item(0);
/** @var \DOMElement $img */
$img->setAttribute('src', Url::fromPath($img->getAttribute('src'))->getAbsoluteUrl());
$path = $this->getView()->getHelper('Url')->url(
array_merge(
array(
'image' => $img->getAttribute('src')
),
$this->urlParams
),
$this->imageUrl,
false,
false
);
$url = $this->getView()->url($path);
/** @var \Icinga\Web\Url $url */
$img->setAttribute('src', $url->getAbsoluteUrl());
return substr_replace($doc->saveXML($img), '', -2, 1); // Replace '/>' with '>'
}

View File

@ -43,7 +43,22 @@ $docModulePdf = new Zend_Controller_Router_Route(
)
);
$docModuleImg = new Zend_Controller_Router_Route_Regex(
'doc/module/([^/]+)/image/(.+)',
array(
'controller' => 'module',
'action' => 'image',
'module' => 'doc'
),
array(
'moduleName' => 1,
'image' => 2
),
'doc/module/%s/image/%s'
);
$this->addRoute('doc/module/chapter', $docModuleChapter);
$this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter);
$this->addRoute('doc/module/toc', $docModuleToc);
$this->addRoute('doc/module/pdf', $docModulePdf);
$this->addRoute('doc/module/img', $docModuleImg);