diff --git a/src/IDF/Template/Markdown.php b/src/IDF/Template/Markdown.php index c058d63..82b12cd 100644 --- a/src/IDF/Template/Markdown.php +++ b/src/IDF/Template/Markdown.php @@ -55,8 +55,17 @@ class IDF_Template_Markdown extends Pluf_Template_Tag $text = IDF_Template_safePregReplace('#\[\[([A-Za-z0-9\-]+)\]\]#im', array($this, 'callbackWikiPageNoName'), $text); + $filter = new IDF_Template_MarkdownPrefilter(); - echo $filter->go(Pluf_Text_MarkDown_parse($text)); + $text = $filter->go(Pluf_Text_MarkDown_parse($text)); + + // Replace [[!ResourceName]] with corresponding HTML for the resource; + // we need to do that after the HTML filtering as we'd otherwise be unable to use + // certain HTML elements, such as iframes, that are used to display text content + // FIXME: no support for escaping yet in place + echo IDF_Template_safePregReplace('#\[\[!([A-Za-z0-9\-]+)(?:,\s*([^\]]+))?\]\]#im', + array($this, 'callbackWikiResource'), + $text); } function callbackWikiPageNoName($m) @@ -80,6 +89,85 @@ class IDF_Template_Markdown extends Pluf_Template_Tag return ''.$m[1].''; } + function callbackWikiResource($m) + { + @list($match, $resourceName, $opts) = $m; + + if (!$this->request->rights['hasWikiAccess']) { + return ''.$match.''; + } + + $sql = new Pluf_SQL('project=%s AND title=%s', + array($this->project->id, $resourceName)); + $resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen())); + + if ($resources->count() == 0) { + if ($this->request->user->isAnonymous()) { + return ''.$match.''; + } + + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::createResource', + array($this->project->shortname), + array('name' => $resourceName)); + return ' '. + ''.$match.''; + } + + // by default, render the most recent revision + $resourceRevision = $resources[0]->get_current_revision(); + + list($urlConf, $urlMatches) = $this->request->view; + + // if we currently look at an existing wiki page, look up its name and find the proper resource (if any) + if ($urlConf['model'] == 'IDF_Views_Wiki' && $urlConf['method'] == 'viewPage') { + $sql = new Pluf_SQL('project=%s AND title=%s', + array($this->project->id, $urlMatches[2])); + $pages = Pluf::factory('IDF_Wiki_Page')->getList(array('filter'=>$sql->gen())); + if ($pages->count() == 0) throw new Exception('page not found'); + $pageRevision = $pages[0]->get_current_revision(); + + // if we look at an old version of the page, figure out the resource version back then + if (isset($this->request->GET['rev']) and preg_match('/^[0-9]+$/', $this->request->GET['rev'])) { + $pageRevision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_PageRevision', + $this->request->GET['rev']); + if ($pageRevision->wikipage != $pages[0]->id) { + return ''.$match.''; + } + } + + $sql = new Pluf_SQL('wikiresource=%s AND idf_wiki_pagerevision_id=%s', + array($resources[0]->id, $pageRevision->id)); + $resourceRevision = Pluf::factory('IDF_Wiki_ResourceRevision')->getOne( + array('filter' => $sql->gen(), 'view' => 'join_pagerevision')); + + } + + $validOpts = array( + 'align' => '/^(left|right|center)$/', + 'width' => '/^\d+(%|px|em)?$/', + 'height' => '/^\d+(%|px|em)?$/', + 'preview' => '/^yes|no$/', + 'title' => '/.+/', + ); + + $parsedOpts = array(); + // FIXME: no support for escaping yet in place + $opts = preg_split('/\s*,\s*/', $opts, -1, PREG_SPLIT_NO_EMPTY); + foreach ((array)@$opts as $opt) + { + list($key, $value) = preg_split('/\s*=\s*/', $opt, 2); + if (!array_key_exists($key, $validOpts)) { + continue; + } + if (!preg_match($validOpts[$key], $value)) { + continue; + } + $parsedOpts[$key] = $value; + } + + return $resourceRevision->render($parsedOpts); + } + function callbackEmbeddedDoc($m) { $scm = IDF_Scm::get($this->request->project); diff --git a/src/IDF/Wiki/PageRevision.php b/src/IDF/Wiki/PageRevision.php index 295bbd0..2b4c848 100644 --- a/src/IDF/Wiki/PageRevision.php +++ b/src/IDF/Wiki/PageRevision.php @@ -99,6 +99,14 @@ class IDF_Wiki_PageRevision extends Pluf_Model 'type' => 'normal', ), ); + $table = $this->_con->pfx.'idf_wiki_pagerevision_idf_wiki_resourcerevision_assoc'; + $this->_a['views'] = array( + 'join_pagerevision' => + array( + 'join' => 'LEFT JOIN '.$table + .' ON idf_wiki_pagerevision_id=id', + ), + ); } function changedRevision() diff --git a/src/IDF/Wiki/ResourceRevision.php b/src/IDF/Wiki/ResourceRevision.php index 4a0a3b7..13a8642 100644 --- a/src/IDF/Wiki/ResourceRevision.php +++ b/src/IDF/Wiki/ResourceRevision.php @@ -101,6 +101,14 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model 'verbose' => __('creation date'), ), ); + $table = $this->_con->pfx.'idf_wiki_pagerevision_idf_wiki_resourcerevision_assoc'; + $this->_a['views'] = array( + 'join_pagerevision' => + array( + 'join' => 'LEFT JOIN '.$table + .' ON idf_wiki_resourcerevision_id=id', + ), + ); } function __toString() @@ -165,11 +173,22 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model $this->get_wikiresource()->id, $this->id, $this->fileext); } - function getFileURL() + function getViewURL() { + $prj = $this->get_wikiresource()->get_project(); + $resource = $this->get_wikiresource(); + return Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource', + array($prj->shortname, $resource->title), + array('rev' => $this->id)); + } + + function getRawURL($attachment = false) + { + $query = $attachment ? array('attachment' => 1) : array(); $prj = $this->get_wikiresource()->get_project(); return Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::rawResource', - array($prj->shortname, $this->id)); + array($prj->shortname, $this->id), + $query); } /** @@ -195,20 +214,78 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model } /** - * Renders the resource + * Renders the resource with the given view options, including a link to the resource' detail page */ - function render() + function render($opts = array()) { - $url = $this->getFileURL(); + // give some reasonable defaults + $opts = array_merge(array( + 'align' => 'left', + 'width' => '', + 'height' => '', + 'preview' => 'yes', // if possible + 'title' => '', + ), $opts); + + $attrs = array('class="resource-container"'); + $styles = array(); + if (!empty($opts['align'])) { + switch ($opts['align']) { + case 'left': + $styles[] = 'float: left'; + $styles[] = 'margin-right: 10px'; + break; + case 'center': + $styles[] = 'margin: 0 auto 0 auto'; + break; + case 'right': + $styles[] = 'float: right'; + $styles[] = 'margin-left: 10px'; + break; + } + } + if (!empty($opts['width'])) { + $styles[] = 'width:'.$opts['width']; + } + if (!empty($opts['height'])) { + $styles[] = 'height:'.$opts['height']; + } + + $raw = $this->renderRaw(); + $viewUrl = $this->getViewURL(); + $download = ''; + $html = '
'; + if ($opts['preview'] == 'yes' && !empty($raw)) { + $html .= '
'.$raw.'
'."\n"; + } else { + $rawUrl = $this->getRawURL(true); + $download = ''; + } $resource = $this->get_wikiresource(); + $title = $opts['title']; + if (empty($title)) { + $title = $resource->title.' - '.$resource->mime_type.' - '.Pluf_Utils::prettySize($this->filesize); + } + $html .= '
'.$download.''.$title.'
'."\n"; + $html .= '
'; + return $html; + } + + /** + * Renders a raw version of the resource, without any possibilities of formatting or the like + */ + function renderRaw() + { + $resource = $this->get_wikiresource(); + $url = $this->getRawURL(); if (preg_match('#^image/(gif|jpeg|png|tiff)$#', $resource->mime_type)) { - return sprintf('%s', $url, $url, $resource->title); + return sprintf('%s', $url, $resource->title); } if (preg_match('#^text/(xml|html|sgml|javascript|ecmascript|css)$#', $resource->mime_type)) { return sprintf('', $url, $resource->title); } - return __('Unable to render preview for this MIME type.'); + return ''; } } diff --git a/src/IDF/templates/idf/wiki/viewResource.html b/src/IDF/templates/idf/wiki/viewResource.html index 149159d..726f69f 100644 --- a/src/IDF/templates/idf/wiki/viewResource.html +++ b/src/IDF/templates/idf/wiki/viewResource.html @@ -19,13 +19,16 @@ by {$submitter}.{/blocktrans}

{$resource.summary}

-

{$rev.render()|unsafe}

+{assign $preview = $rev.renderRaw()} +{if $preview == ''} + {assign $preview = __('Unable to render preview for this MIME type.')} +{/if} +

{$preview|unsafe}

-{aurl 'url', 'IDF_Views_Wiki::rawResource', array($project.shortname, $rev.id), array('attachment' => 1)} {if ($isOwner or $isAdmin) and !$rev.is_head}{aurl 'url', 'IDF_Views_Wiki::deleteResourceRev', array($project.shortname, $rev.id)}

{trans 'Trash'} {trans 'Delete this revision'}

diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index 4ccf0ca..5454e0c 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -928,6 +928,40 @@ ol > li { margin-left: 2em; } +.resource-container { + border: 1px solid #EEE; + padding: 5px; +} + +.resource-container .preview { + margin-bottom: 5px; +} + +.resource-container .preview * { + width: 100%; + height: 100%; +} + +.resource-container .preview img { + height: auto; +} + +.resource-container .preview + .title { + font-size: 80%; +} + +.resource-container .title * { + vertical-align: middle; +} + +.resource-container .title .download { + display: inline-block; + margin-right: 5px; + background: url("../img/down-large.png") no-repeat; + width: 22px; + height: 22px; +} + /** * main menu */