diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php
index b6b2d6375..5544dbce4 100644
--- a/modules/doc/application/controllers/ModuleController.php
+++ b/modules/doc/application/controllers/ModuleController.php
@@ -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
*
diff --git a/modules/doc/doc/1-module-documentation.md b/modules/doc/doc/1-module-documentation.md
index edf20aac2..2334324a9 100644
--- a/modules/doc/doc/1-module-documentation.md
+++ b/modules/doc/doc/1-module-documentation.md
@@ -1,6 +1,6 @@
# Writing Module Documentation
-
+
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.
## 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.:
- 
+ 
diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php
index acd085d04..5f5438ed8 100644
--- a/modules/doc/library/Doc/DocController.php
+++ b/modules/doc/library/Doc/DocController.php
@@ -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,
diff --git a/modules/doc/library/Doc/Renderer/DocRenderer.php b/modules/doc/library/Doc/Renderer/DocRenderer.php
index feb420e91..3662fd574 100644
--- a/modules/doc/library/Doc/Renderer/DocRenderer.php
+++ b/modules/doc/library/Doc/Renderer/DocRenderer.php
@@ -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
*
diff --git a/modules/doc/library/Doc/Renderer/DocSectionRenderer.php b/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
index a39d24a77..9bc8bfee0 100644
--- a/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
+++ b/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
@@ -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 '>'
}
diff --git a/modules/doc/run.php b/modules/doc/run.php
index 9d49b89d4..df9dd0930 100644
--- a/modules/doc/run.php
+++ b/modules/doc/run.php
@@ -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);