Gallery Plugin

gallery plugin by Andreas Gohr
This Syntax Plugin allows you to embed an automatically created image gallery into a page.

Last updated on 2009-11-02. Provides Syntax.
Compatible with DokuWiki 2009-12-25+.

Conflicts with galleryv1.5!
Similar to gallery2, galleryv1.5, jalbum.

Tagged with embed, feed, gallery, images, listing, media, namespace.

Download

Use the download link given above in the plugin manager or to download the plugin manually.

Changes

Syntax

A basic gallery can be added by selecting a namespace like this:

{{gallery>namespace}}

All imagefiles in the selected namespace will be added to the image gallery. Don't forget the ”:” in front of the namespace.

Instead of using a whole namespace of images, you can also specify a single image – this makes most sense when combined with the lightbox mode (see below).

{{gallery>namespace:someimage.jpg}}

The created gallery can be aligned by using whitespace (defaults to centered):

{{gallery> namespace}} (right aligned)
{{gallery>namespace }} (left aligned)
{{gallery> namespace }} (centered)

Instead of a namespace, you can also give an HTTP(s) URL to any Media RSS or ATOM feed with enclosures (as produced by most photo sharing sites like Flickr). The images will then be pulled from that feed instead:

{{gallery>http://www.23hq.com/rss/schabloni}}

Note: since the question mark is used to separate the parameters (see next section) the URL can not contain any question mark. To use such a feed URL with the gallery plugin, just use one of the many short URL services like http://bit.ly.

E.g. instead of http://api.flickr.com/services/feeds/photos_public.gne?id=22019303@N00&lang=en-us&format=rss_200 use a shortened URL like http://bit.ly/HurZM.

Additionally, to have thumbnail creation correctly working you need to set fetchsize big enough to get the remote images downloaded.

Parameters

A number of parameters can be set by appending them with ? character to the namespace or image. Each parameter needs to be separated with a & character. Defaults for all parameters can be set in the config manager. If a parameter is enabled by default it can be disabled in the syntax by prefixing it with the syllable no. E.g. the parameter cache is usually enabled and can be disabled using the keyword nocache. Below is a list of all recognized parameters

Parameter Default Description
<number>x<number> 120x120 Sets the size for thumbnails. Unless the crop option is set, this is a boundary box into which the thumbnail will be fitted, maintaining the correct aspect ratio.
<number>X<number> 800X600 Sets the size for the linked images in direct mode. This is a boundary box into which the image will be fitted, maintaining the correct aspect ratio. Note the uppercase X.
<number> 5 The number images per row in the gallery table. If you specify a 0 no table is used instead all thumbnails are added in a sequence.
=<number> =0 Limits the output to the given number of images. 0 means all.
+<number> +0 Skip the first number of images. Useful with the option above.
~<number> ~0 Add a pagination for the thumbnails displaying the number of given thumbnails per page. 0 disables pagination. Pagination is added through JavaScript - when no JavaScript is available all thumbnails are displayed
cache enabled Usually the output of the created gallery is cached. When the images in your selected namespace change, you have to manually force an update of the gallery page's cache. To disable the cache for the page showing the gallery, set nocache as option.
crop disabled Make thumbnails the exact given thumbnail size big, cropping when needed.
direct disabled Link thumbnails with the bigger sized images not with their detail page
lightbox disabled Show images in a fancy JavaScript modal browsing window, see below for details. Setting this option automatically implies the direct mode
reverse disabled Reverse the order of the displayed images
random disabled Sort images randomly. You might want to use nocache as well
modsort disabled Sort images by file modification date
datesort disabled Sort images by EXIF date
titlesort disabled Sort images by EXIF title
showname disabled Show filename below thumbnails
showtitle disabled Show EXIF title below thumbnails
anything containing a * jpg,gif,png images This can be used to filter the list of files found in the given namespace. * work as simple wildcard symbol.

Example:

{{gallery>images:vacation?image_*.jpg&80x80&crop&lightbox}}

This displays all images beginning with image_ and ending in .jpg from the namespace images:vacation. Thumbnails are cropped to 80×80 pixels and images will be opened in lightbox mode.

About the Lightbox mode

This mode will open the clicked picture inside the current browser window without leaving the current page1). You can close the picture view by clicking the little X in the upper right corner or anywhere in the picture. You can move to the next or previous image by using the arrow buttons in the lower corners.

The picture is downsized if necessary to fit into the current browser window. You can enlarge it with the arrow button in the top corner.

The following keys can be used to navigate:

Key Action
or n next image
or p previous image
x or c or ESC close the image view

Note: The feature does not use Version 2 of the Lightbox script because of its heavy and DokuWiki-incompatible dependencies.

Known Limitations and Caveats

Uploading Images

Uploading images is beyond the scope of this plugin. Do not request any features regarding this.

  • Use the Mediamanager to upload images one by one
  • Use the archiveupload plugin to upload multiple images in a Zip file
  • Use the Flash Uploader to upload multiple files
  • Upload the files manually via FTP to the data/media directory. Keep in mind that image names need to be valid pagenames, all lowercase, no spaces or special chars!

Caching

The gallery output is cached by default. When you add pictures later, they may not show up in the gallery: add &purge=true to the end of the URL to clear the cache. See caching for details.

Optionally use the nocache parameter of the plugin (not recommended).

EXIF Data Problems

Problems with accessing EXIF or IPTC data in the images, should be reported as DokuWiki bugs and not for this plugin. All EXIF handling is in DokuWiki core. Currently EXIF Data is expected in UTF-8 encoding. There are some problems fetching the correct date from images, these are fixed in the current development version of DokuWiki.

Lightbox Problems

When the lightbox mode doesn't work and instead images are simply opened in the same window, the JavaScript was not correctly loaded. This is most likely a Browser-Cache issue. Simply follow the steps described for fixing a similar problem with the toolbar.

Images are not Resized

When no thumbnails are created, then your PHP install is missing the libGD extension. Either install the extension or configure DokuWiki to use imagemagick instead.

MediaRSS feed displays "nothing found"

When no images from your feed are shown, be sure you don't have a question mark in your URL. Use an URL shortener as suggested above. Also be sure your feed URL (before shortening) starts with http:// or https:// and not with feed://. The latter is just a renamed HTTP link - simply rename it back.

External Images are not Resized

As written above, you need to increase the fetchsize config option. Also make sure LibGD or ImageMagick are installed.

Demo Installations

Discussion

Be sure to read the Known Limitations and Caveats section before asking questions here.

I dribble for an option that would take not only the pictures in the namespace, but also all the images in all the subnamespaces.
A single additional parameter would be ”-<number>”, with ”-1” by default, ”-0” for full depth. Would it be easy to script in PHP? – Laurent Chabin 2009/08/21

Here is the diff that does that (translation provided only in fr and en) – 2009/08/21
Diff with gallery-2009-06-27
Patch to be applied from the lib/plugins/gallery directory

--- conf/default.php	Fri Dec 19 02:38:38 2008
+++ conf/default.php	Fri Aug 21 21:44:43 2009
@@ -10,6 +10,7 @@
 $conf['image_width']      = 800;
 $conf['image_height']     = 600;
 $conf['cols']             = 5;
+$conf['depth']            = 1;
 
 $conf['sort'] = 'file';
 $conf['options'] = 'cache';
--- conf/metadata.php	Fri Dec 19 02:38:38 2008
+++ conf/metadata.php	Fri Aug 21 21:34:59 2009
@@ -10,6 +10,7 @@
 $meta['image_width']      = array('numeric');
 $meta['image_height']     = array('numeric');
 $meta['cols']             = array('numeric');
+$meta['depth']            = array('numeric');
 
 $meta['sort']    = array('multichoice', '_choices' => array('file','mod','date','title'));
 $meta['options'] = array('multicheckbox', '_choices' => array('cache','crop','direct','lightbox','random','reverse','showname','showtitle'));
--- lang/en/settings.php	Fri Dec 19 02:38:38 2008
+++ lang/en/settings.php	Fri Aug 21 21:43:34 2009
@@ -11,6 +11,7 @@
 $lang['image_width']      = 'Image width';
 $lang['image_height']     = 'Image height';
 $lang['cols']             = 'Images per row';
+$lang['depth']            = 'Namespaces search depth (0 for full, 1 for no depth, 2 or more: recursive search)';
 
 $lang['sort']         = 'How to sort the gallery images';
 $lang['sort_o_file']  = 'sort by filename';
--- lang/fr/settings.php	Fri Dec 19 02:38:38 2008
+++ lang/fr/settings.php	Fri Aug 21 21:43:11 2009
@@ -11,3 +11,4 @@
 $lang['image_width']      = 'Largeur des images';
 $lang['image_height']     = 'Hauteur des images';
 $lang['cols']             = 'Nombre d\'images par ligne';
+$lang['depth']            = 'Profondeur de recherche en namespaces (0: complete, 1: aucune, 2 ou plus: recherche recursive à la profondeur  )';
--- syntax.php	Sat Jun 27 04:30:16 2009
+++ syntax.php	Sat Aug 22 00:11:56 2009
@@ -81,6 +81,7 @@
         $data['iw']       = $this->getConf('image_width');
         $data['ih']       = $this->getConf('image_height');
         $data['cols']     = $this->getConf('cols');
+        $data['depth']    = $this->getConf('depth');
         $data['filter']   = '';
         $data['lightbox'] = false;
         $data['direct']   = false;
@@ -111,6 +112,8 @@
                 $data['limit'] = $match[1];
             }elseif(preg_match('/^\+(\d+)$/',$param,$match)){
                 $data['offset'] = $match[1];
+            }elseif(preg_match('/^\-(\d+)$/',$param,$match)){
+                $data['depth'] = $match[1];
             }elseif(is_numeric($param)){
                 $data['cols'] = (int) $param;
             }elseif(preg_match('/^~(\d+)$/',$param,$match)){
@@ -154,12 +157,60 @@
         return true;
     }
 
+	/**
+	 * Very small Tweak of search.php : search_media()
+	 * List all media files in a namespace and subnamespaces down to a certain depth.
+	 * To be used with search.php : search().
+	 *
+	 * @author  Andreas Gohr <andi@splitbrain.org>
+	 * @author  Laurent Chabin <lcdvasrm@free.fr>
+	 */
+	function _gallery_search_media(&$data,$base,$file,$type,$lvl,$opts){
+	  //we do nothing with directories
+	  if($type == 'd') {
+		return ($opts['recursive'] && ($opts['depth']!=$lvl));
+	  }
+
+	  $info         = array();
+	  $info['id']   = pathID($file,true);
+	  if($info['id'] != cleanID($info['id'])){
+		if($opts['showmsg'])
+		  msg(hsc($info['id']).' is not a valid file name for DokuWiki - skipped',-1);
+		return false; // skip non-valid files
+	  }
+
+	  //check ACL for namespace (we have no ACL for media files)
+	  if(auth_quickaclcheck(getNS($info['id']).':*') < AUTH_READ){
+		return false;
+	  }
+
+	  $info['file'] = basename($file);
+	  $info['size'] = filesize($base.'/'.$file);
+	  $info['mtime'] = filemtime($base.'/'.$file);
+	  $info['writable'] = is_writable($base.'/'.$file);
+	  if(preg_match("/\.(jpe?g|gif|png)$/",$file)){
+		$info['isimg'] = true;
+		require_once(DOKU_INC.'inc/JpegMeta.php');
+		$info['meta']  = new JpegMeta($base.'/'.$file);
+	  }else{
+		$info['isimg'] = false;
+	  }
+	  $data[] = $info;
+
+	  return false;
+	}
+
+
     /**
      * Gather all photos matching the given criteria
      */
     function _findimages(&$data){
         global $conf;
         $files = array();
+        $opts = array();
+        if ($data['depth']!=1) { $opts['recursive'] = true; } else { $opts['recursive'] = false; }
+
+        $opts['depth'] = $data['depth'];
 
         $dir = utf8_encodeFN(str_replace(':','/',$data['ns']));
 
@@ -175,7 +226,7 @@
             );
             $data['_single'] = true;
         }else{
-            search($files,$conf['mediadir'],'search_media',array(),$dir);
+            search($files,$conf['mediadir'],array($this,'_gallery_search_media'),$opts,$dir);
             $data['_single'] = false;
         }
 

If multiple galleries are added to a page, the lightbox popup will scroll through all of the images in all of them.
I would have expected it to only scroll through the images in the gallery where the first thumbnail was initially clicked to bring up the enlargement. –2009/06/23

Comments for Images

Use this syntax.php if you want comments for your images.

<gallery images:vacation?image_*.jpg&80x80&crop&lightbox>
image.jpg=Comment for image 
image2.jpg=second comment for another image 
</gallery>
<?php
/**
 * Embed an image gallery
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Andreas Gohr <andi@splitbrain.org>
 * @author     Joe Lapp <joe.lapp@pobox.com>
 * @author     Dave Doyle <davedoyle.canadalawbook.ca>
 * @author     Gerry Weissbach <gerry.w@gammaproduction.de>
 */

if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
require_once(DOKU_INC.'inc/search.php');
require_once(DOKU_INC.'inc/JpegMeta.php');

class syntax_plugin_gallery extends DokuWiki_Syntax_Plugin {
    /**
     * return some info
     */
    function getInfo(){
        return array(
            'author' => 'Andreas Gohr',
            'email'  => 'andi@splitbrain.org',
            'date'   => '2007-11-06',
            'name'   => 'Gallery Plugin',
            'desc'   => 'Creates a gallery of images from a namespace',
            'url'    => 'http://wiki.splitbrain.org/plugin:gallery',
        );
    }

    /**
     * What kind of syntax are we?
     */
    function getType(){
        return 'substition';
    }

    /**
     * What about paragraphs?
     */
    function getPType(){
        return 'block';
    }

    /**
     * Where to sort in?
     */
    function getSort(){
        return 301;
    }


    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {
        //$this->Lexer->addSpecialPattern('\{\{gallery>[^}]*\}\}',$mode,'plugin_gallery');
        $this->Lexer->addSpecialPattern('<gallery.*?>.+?</gallery>',$mode,'plugin_gallery');
    }


    /**
     * Parse option
     */
    function parseOpt($params, $name) {
        if(preg_match('/\b'.$name.'\b/i',$params,$match)) {
	    return true;
        }else if(preg_match('/\bno'.$name.'\b/i',$params,$match)) {
	    return false;
	}else{
            return $this->getConf($name);
        }
    }

    /**
     * Handle the match
     */
    function handle($all_match, $state, $pos, &$handler){
        //$match = substr($match,10,-2); //strip markup from start and end

        $data = array();

        //handle params
        //list($ns,$params) = explode('?',$match,2);
	preg_match("#<gallery(.*?)>#", $all_match, $para);
	list($ns,$params) = explode('?',$para[1],2);

        /*if ( substr($ns, 0, 1) == ' ' )
        	$data['float']="right";
        else if ( substr($ns, -1) == ' ' )
        	$data['float']="left";
        else
        	$data['float'] = null;
	*/
        //namespace
        $data['ns'] = $ns;

        //max thumb dimensions
        if(preg_match('/\b(\d+)x(\d+)\b/',$params,$match)){
            $data['w'] = $match[1];
            $data['h'] = $match[2];
        }else{
            $data['w'] = $this->getConf('thumbnail_width');
            $data['h'] = $this->getConf('thumbnail_height');
        }

        //max lightbox dimensions
        if(preg_match('/\b(\d+)X(\d+)\b/',$params,$match)){
            $data['w_lightbox'] = $match[1];
            $data['h_lightbox'] = $match[2];
        }else{
            $data['w_lightbox'] = $this->getConf('image_width');
            $data['h_lightbox'] = $this->getConf('image_height');
        }

        //number of images per row
        if(preg_match('/\b(\d+)\b/i',$params,$match)){
            $data['cols'] = $match[1];
        }else{
            $data['cols'] = $this->getConf('cols');
        }

        //show the filename
		$data['showname'] = $this->parseOpt($params, 'showname');

        //lightbox style?
		$data['lightbox'] = $this->parseOpt($params, 'lightbox');
	
        //direct linking?
		if($data['lightbox']) {
            $data['direct']   = true; //implicit direct linking
		}else{
	    	$data['direct'] = $this->parseOpt($params, 'direct');
		}

        //reverse sort?
		$data['reverse'] = $this->parseOpt($params, 'reverse');

        //resize thing?
        if(preg_match('/\bnoresize\b/i',$params,$match)) {
            $data['noresize'] = true;
        }else{
            $data['noresize'] = false;
        }

        //Layout thing?
        if(preg_match('/\bmagazine\b/i',$params,$match)) {
            $data['magazine'] = true;
        }else{
            $data['magazine'] = false;
        }

        //Layout thing?
        if(preg_match('/\bdiashow\b/i',$params,$match)) {
            $data['diashow'] = true;
        }else{
            $data['diashow'] = false;
        }

        //Layout thing?
        if(preg_match('/\bpageAmount=(\d+)\b/i',$params,$match)) {
            $data['pageAmount'] = $match[1];
        }else{
            $data['pageAmount'] = -1;
        }

	$match    = preg_replace('#<gallery.*?>|</gallery>#', '', $all_match);
	$match = preg_split ('#\n#',$match);
	foreach ($match as $line){
		list($name,$comment) = explode('=',$line,2);	
		$data['comments'][$name]=$comment;    
	}
                        
        return $data;
    }

    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
		if ( $data['pageAmount'] > 0 ) $renderer->info['cache'] = FALSE;

        if($mode == 'xhtml'){
            $renderer->doc .= $this->_gallery($data);
            return true;
        }
        return false;
    }

    /**
     * Does the gallery formatting
     */
    function _gallery($data){
        global $conf;
        global $lang;
        $ret = '';

        //use the search to get all files
        $ns = cleanID($data['ns']);
        $dir = utf8_encodeFN(str_replace(':','/',$ns));
        $files = array();
        search($files,$conf['mediadir'],'search_media',array(),$dir);

        //anything found?
        if(!count($files)){
            $ret .= '<div class="nothing"><pre>'.$lang['nothingfound'].print_r($data,$return=true).'</pre></div>';
            return $ret;
        }

        //reverse if wanted
        if($data['reverse']) rsort($files);
        
        //startPoint of Pages
        $startPoint = intval($_REQUEST['startPoint']);
       

		// build magazine
		if ($data['magazine'])
		{
			require_once(realpath(dirname(__FILE__)).'/inc/magazinelayout.class.php');

			$template = '<img src="/lib/plugins/gallery/inc/image.php?size=[size]&file=[image]" alt="" />';
			$mag = new magazinelayout($data['w'], $data['h']!=120?$data['h']:5, $template);
			$cols = $data['cols'];
			$cols = $cols > 8 ? 8 : $cols;
			
//			$content = '<div class="gallery">';

			$amount = 0;
			foreach($files as $img){
				if(!$img['isimg']) continue;
				$amount ++;
				if ( $amount < $startPoint ) continue;
				if ( $data['pageAmount'] > 0 && $amount-$startPoint >= $data['pageAmount'] ) break;
			
				$img['lightbox'] = $data['lightbox'];
				$img['h_lightbox'] = $data['h_lightbox'];
				$img['w_lightbox'] = $data['w_lightbox'];
				$img['direct'] =  $data['direct'];
				$img['float'] =  $data['float'];
			
				$mag->addImage($img);
			
				$cols --;
				if ($cols <= 0 )
				{
					$content .= $mag->getHtml();
					$mag = new magazinelayout($data['w'], $data['h']!=120?$data['h']:5, $template);
					$cols = $data['cols'];
				}
				
			}
		
			if ($mag->_numimages > 0) $content .= $mag->getHtml();
		
//			$content .= '<br style="clear:both" /></div></div></div>';

			$content .= $this->_pageSelect(count($files), $data['pageAmount'], $startPoint);
			return $content;
		}


        // build gallery
        if($data['cols'] > 0){ // format as table
            $ret .= '<table class="gallery"' . (!empty($data['float']) ? ' style="float: '.$data['float'].';"' : '') . '>';
            $i = 0;
            $amount = 0;
            foreach($files as $img){
                if(!$img['isimg']) continue;
				$amount ++;
				if ( $amount < $startPoint ) continue;
				if ( $data['pageAmount'] > 0 && $amount-$startPoint >= $data['pageAmount'] ) break;

                if($i == 0){
                    $ret .= '<tr>';
                }

                $ret .= '<td>';
                $ret .= $this->_image($img,$data);
                $ret .= $this->_showname($img,$data);
		$ret .= $this->_showcomment($img,$data);
                $ret .= '</td>';

                $i++;

                $close_tr = true;
                if($i == $data['cols']){
                    $ret .= '</tr>';
                    $close_tr = false;
                    $i = 0;
                }
            }

            if ($close_tr){
                // add remaining empty cells
                for(;$i < $data['cols']; $i++){
                    $ret .= '<td></td>';
                }
                $ret .= '</tr>';
            }

            $ret .= '</table>';
        }else{ // format as div sequence
            $ret .= '<div class="gallery"' . (!empty($data['float']) ? ' style="float: '.$data['float'].';"' : '') . '>';

			$amount = 0;
            foreach($files as $img){
                if(!$img['isimg']) continue;
				$amount ++;
				if ( $amount < $startPoint ) continue;
				if ( $data['pageAmount'] > 0 && $amount-$startPoint >= $data['pageAmount'] ) break;

                $ret .= '<div>';
                $ret .= $this->_image($img,$data);
                $ret .= $this->_showname($img,$data);
		$ret .= $this->_showcomment($img,$data);
                $ret .= '</div> ';
            }

            $ret .= '<br style="clear:both" /></div>';
        }

		$ret .= $this->_pageSelect(count($files), $data['pageAmount'], $startPoint);
        return $ret;
    }

    /**
     * Defines how a thumbnail should look like
     */
    function _image($img,$data){
        global $ID;

        $w = $img['meta']->getField('File.Width');
        $h = $img['meta']->getField('File.Height');
        $dim = array();
        
        if ( $data['noresize'])
        {
            $w = $data['w'];
            $h = $data['h'];
        }
        else if($w > $data['w'] || $h > $data['h']){
            $ratio = $img['meta']->getResizeRatio($data['w'],$data['h']);
            $w = floor($w * $ratio);
            $h = floor($h * $ratio);
            $dim = array('w'=>$w,'h'=>$h);
        }

        //prepare img attributes
        $i           = array();
        $i['width']  = $w;
        $i['height'] = $h;
        $i['border'] = 0;
        $i['alt']    = $img['meta']->getField('Simple.Title');
        $i['class']  = 'tn';
        $iatt = buildAttributes($i);
        $src  = ml($img['id'],$dim);

        // prepare lightbox dimensions
        $w_lightbox = $img['meta']->getField('File.Width');
        $h_lightbox = $img['meta']->getField('File.Height');
        $dim_lightbox = array();
        if($w_lightbox > $data['w_lightbox'] || $h_lightbox > $data['h_lightbox']){
            $ratio = $img['meta']->getResizeRatio($data['w_lightbox'],$data['h_lightbox']);
            $w_lightbox = floor($w_lightbox * $ratio);
            $h_lightbox = floor($h_lightbox * $ratio);
            $dim_lightbox = array('w'=>$w_lightbox,'h'=>$h_lightbox);
        }

        //prepare link attributes
        $a           = array();
        $a['title']  = $img['meta']->getField('Simple.Title');
        if($data['lightbox']){
            $href   = ml($img['id'],$dim_lightbox);
            $a['class'] = "lightbox JSnocheck";
            $a['rel']   = "lightbox";
        }else{
            $href   = ml($img['id'],array('id'=>$ID),$data['direct']);
        }
        $aatt = buildAttributes($a);

        // prepare output
        $ret  = '';
        $ret .= '<a href="'.$href.'" '.$aatt.'>';
        $ret .= '<img src="'.$src.'" '.$iatt.' />';
        $ret .= '</a>';
        return $ret;
    }


    /**
     * Defines how a filename + link should look
     */
    function _showname($img,$data){
        global $ID;

        if(!$data['showname']) { return ''; }

        //prepare link
        $lnk = ml($img['id'],array('id'=>$ID),false);

        // prepare output
        $ret  = '';
        $ret .= '<br /><a href="'.$lnk.'">';
        $ret .= $img['file']; // see fix posted on the wiki
        $ret .= '</a>';
        return $ret;
    }

    /**
     * Defines how a comment should look
     */
    function _showcomment($img,$data){
        global $ID;

        // prepare output
        $ret  = '';
        $ret .= '<br />';
        $ret .= $data['comments'][$img['file']]; 
      
        return $ret;
    }
    
    function _pageSelect($fileCount, $showCount, $startPoint)
    {
    	global $ID;
    	$content = '<div id="pageSelect">';
    	
    	$pages = ceil($fileCount / $showCount);
    	if ( $pages <= 1 ) return "";
    	
    	for ($i=0; $i<$pages; $i++)
    	{
	        $lnk = wl($ID, array('startPoint'=>$i*$showCount));
	        $content .= '<a href="' . $lnk . '">';
	        $content .= $i+1;
	        $content .= '</a>';
	        
	        if ( $i < $pages -1 )
	        	$content .= '&nbsp;-&nbsp;';
    	}
    	
    	$content .= '</div>';
    	return $content;
    }
}

//Setup VIM: ex: et ts=4 enc=utf-8 :

Alternative text for Images on HTML tags

author     : Javier Espinosa Alfonso
email      : jespinosa@inetnova.com
description: Gallery image plugin modified to enable edit alternative text of a image.
type       : syntax
lastupdate : 2009-08-18
compatible : 2008-05-05


Edit text alternative for a image of the gallery in HTML tags: a & img. Like this:
<img alt=“Text alternative” src=“image1.png”>
<a title=“Title link” href=“go.html”>Go</a>

Examples:

{{gallery>:namespace:?image_*.png&lightbox|All images with the same text alternative}}
{{gallery>:namespace:image_123.png|Alternative text without params}}
{{gallery>:namespace:image_123.png?&lightbox|Alternative text with params}}
{{gallery>::namespace:image_123.png}} // Default value
<?php
/**
 * Embed an image gallery
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Andreas Gohr <andi@splitbrain.org>
 * @author     Joe Lapp <joe.lapp@pobox.com>
 * @author     Dave Doyle <davedoyle.canadalawbook.ca>
 * @author     Javier Espinosa <jespinosa@inetnova.com | blog.virunner.com/javier-espinosa/>(changes with @JEA)
 */

if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
require_once(DOKU_INC.'inc/search.php');
require_once(DOKU_INC.'inc/JpegMeta.php');

class syntax_plugin_gallery extends DokuWiki_Syntax_Plugin {

    /**
     * return some info
     */
    function getInfo(){
        return confToHash(dirname(__FILE__).'/info.txt');
    }

    /**
     * What kind of syntax are we?
     */
    function getType(){
        return 'substition';
    }

    /**
     * What about paragraphs?
     */
    function getPType(){
        return 'block';
    }

    /**
     * Where to sort in?
     */
    function getSort(){
        return 301;
    }


    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {
        $this->Lexer->addSpecialPattern('\{\{gallery>[^}]*\}\}',$mode,'plugin_gallery');
    }

    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
        global $ID;
        $match = substr($match,10,-2); //strip markup from start and end

        $data = array();

        $data['galid'] = substr(md5($match),0,4);

        // alignment
        $data['align'] = 0;
        if(substr($match,0,1) == ' ') $data['align'] += 1;
        if(substr($match,-1,1) == ' ') $data['align'] += 2;

// @JEA Begin
        // extract title
        list($match,$title) = explode('|',$match,2);
// @JEA End

        // extract params
        list($ns,$params) = explode('?',$match,2);
        $ns = trim($ns);

        // namespace (including resolving relatives)
        $data['ns'] = resolve_id(getNS($ID),$ns);

        // set the defaults
        $data['tw']       = $this->getConf('thumbnail_width');
        $data['th']       = $this->getConf('thumbnail_height');
        $data['iw']       = $this->getConf('image_width');
        $data['ih']       = $this->getConf('image_height');
        $data['cols']     = $this->getConf('cols');
        $data['filter']   = '';
        $data['lightbox'] = false;
        $data['direct']   = false;
        $data['showname'] = false;
        $data['showtitle'] = false;
        $data['reverse']  = false;
        $data['random']   = false;
        $data['cache']    = true;
        $data['crop']     = false;
        $data['sort']     = $this->getConf('sort');
        $data['limit']    = 0;
        $data['offset']   = 0;
        $data['paginate'] = 0;
// @JEA Begin
				// default value (can be changed)
        $data['title']    = 'No title';
        if (!empty($title)){
            $data['title'] = $title;
        }
// @JEA End

        // parse additional options
        $params = $this->getConf('options').','.$params;
        $params = preg_replace('/[,&\?]+/',' ',$params);
        $params = explode(' ',$params);
        foreach($params as $param){
            if($param === '') continue;
            if($param == 'titlesort'){
                $data['sort'] = 'title';
            }elseif($param == 'datesort'){
                $data['sort'] = 'date';
            }elseif($param == 'modsort'){
                $data['sort'] = 'mod';
            }elseif(preg_match('/^=(\d+)$/',$param,$match)){
                $data['limit'] = $match[1];
            }elseif(preg_match('/^\+(\d+)$/',$param,$match)){
                $data['offset'] = $match[1];
            }elseif(is_numeric($param)){
                $data['cols'] = (int) $param;
            }elseif(preg_match('/^~(\d+)$/',$param,$match)){
                $data['paginate'] = $match[1];
            }elseif(preg_match('/^(\d+)([xX])(\d+)$/',$param,$match)){
                if($match[2] == 'X'){
                    $data['iw'] = $match[1];
                    $data['ih'] = $match[3];
                }else{
                    $data['tw'] = $match[1];
                    $data['th'] = $match[3];
                }
            }elseif(strpos($param,'*') !== false){
                $param = preg_quote($param,'/');
                $param = '/^'.str_replace('\\*','.*?',$param).'$/';
                $data['filter'] = $param;
            }else{
                if(substr($param,0,2) == 'no'){
                    $data[substr($param,2)] = false;
                }else{
                    $data[$param] = true;
                }
            }
        }

        // implicit direct linking?
        if($data['lightbox']) $data['direct']   = true;


        return $data;
    }

    /**
     * Create output
     */
    function render($mode, &$R, $data) {
        if($mode != 'xhtml') return false;

        $R->info['cache'] = $data['cache'];
        $R->doc .= $this->_gallery($data);
        return true;
    }

    /**
     * Gather all photos matching the given criteria
     */
    function _findimages(&$data){
        global $conf;
        $files = array();

        $dir = utf8_encodeFN(str_replace(':','/',$data['ns']));

        // all possible images for the given namespace (or a single image)
        if(is_file($conf['mediadir'].'/'.$dir)){
            require_once(DOKU_INC.'inc/JpegMeta.php');
            $files[] = array(
                'id'    => $data['ns'],
                'isimg' => preg_match('/\.(jpe?g|gif|png)$/',$dir),
                'file'  => basename($dir),
                'mtime' => filemtime($conf['mediadir'].'/'.$dir),
                'meta'  => new JpegMeta($conf['mediadir'].'/'.$dir)
            );
            $data['_single'] = true;
        }else{
            search($files,$conf['mediadir'],'search_media',array(),$dir);
            $data['_single'] = false;
        }

        // done, yet?
        $len = count($files);
        if(!$len) return $files;
        if($data['single']) return $files;

        // filter images
        for($i=0; $i<$len; $i++){
            if(!$files[$i]['isimg']){
                unset($files[$i]); // this is faster, because RE was done before
            }elseif($data['filter']){
                if(!preg_match($data['filter'],noNS($files[$i]['id']))) unset($files[$i]);
            }
        }
        if($len<1) return $files;

        // random?
        if($data['random']){
            shuffle($files);
        }else{
            // sort?
            if($data['sort'] == 'date'){
                usort($files,array($this,'_datesort'));
            }elseif($data['sort'] == 'mod'){
                usort($files,array($this,'_modsort'));
            }elseif($data['sort'] == 'title'){
                usort($files,array($this,'_titlesort'));
            }

            // reverse?
            if($data['reverse']) $files = array_reverse($files);
        }

        // limits and offsets?
        if($data['offset']) $files = array_slice($files,$data['offset']);
        if($data['limit']) $files = array_slice($files,0,$data['limit']);

        return $files;
    }

    /**
     * usort callback to sort by file lastmodified time
     */
    function _modsort($a,$b){
        if($a['mtime'] < $b['mtime']) return -1;
        if($a['mtime'] > $b['mtime']) return 1;
        return strcmp($a['file'],$b['file']);
    }

    /**
     * usort callback to sort by EXIF date
     */
    function _datesort($a,$b){
        $da = $a['meta']->getDateField('EarliestTime');
        $db = $b['meta']->getDateField('EarliestTime');
        if($da < $db) return -1;
        if($da > $db) return 1;
        return strcmp($a['file'],$b['file']);
    }

    /**
     * usort callback to sort by EXIF title
     */
    function _titlesort($a,$b){
        $ta = $a['meta']->getField('Simple.Title');
        $tb = $b['meta']->getField('Simple.Title');
        return strcmp($ta,$tb);
    }


    /**
     * Does the gallery formatting
     */
    function _gallery($data){
        global $conf;
        global $lang;
        $ret = '';

        $files = $this->_findimages($data);

        //anything found?
        if(!count($files)){
            $ret .= '<div class="nothing">'.$lang['nothingfound'].'</div>';
            return $ret;
        }

        // prepare alignment
        $align = '';
        $xalign = '';
        if($data['align'] == 1){
            $align  = ' gallery_right';
            $xalign = ' align="right"';
        }
        if($data['align'] == 2){
            $align  = ' gallery_left';
            $xalign = ' align="left"';
        }
        if($data['align'] == 3){
            $align  = ' gallery_center';
            $xalign = ' align="center"';
        }
        if(!$data['_single']){
            if(!$align) $align = ' gallery_center'; // center galleries on default
            if(!$xalign) $xalign = ' align="center"';
        }

        $page = 0;

        // build gallery
        if($data['_single']){
            $ret .= $this->_image($files[0],$data);
            $ret .= $this->_showname($files[0],$data);
            $ret .= $this->_showtitle($files[0],$data);
        }elseif($data['cols'] > 0){ // format as table
            $close_pg = false;

            $i = 0;
            foreach($files as $img){

                // new page?
                if($data['paginate'] && ($i % $data['paginate'] == 0)){
                     $ret .= '<div class="gallery_page gallery__'.$data['galid'].'" id="gallery__'.$data['galid'].'_'.(++$page).'">';
                     $close_pg = true;
                }

                // new table?
                if($i == 0 || ($data['paginate'] && ($i % $data['paginate'] == 0))){
                    $ret .= '<table>';

                }

                // new row?
                if($i % $data['cols'] == 0){
                    $ret .= '<tr>';
                }

                // an image cell
                $ret .= '<td>';
                $ret .= $this->_image($img,$data);
                $ret .= $this->_showname($img,$data);
                $ret .= $this->_showtitle($img,$data);
                $ret .= '</td>';
                $i++;

                // done with this row? cloase it
                $close_tr = true;
                if($i % $data['cols'] == 0){
                    $ret .= '</tr>';
                    $close_tr = false;
                }

                // close current page and table
                if($data['paginate'] && ($i % $data['paginate'] == 0)){
                    if ($close_tr){
                        // add remaining empty cells
                        while($i % $data['cols']){
                            $ret .= '<td></td>';
                            $i++;
                        }
                        $ret .= '</tr>';
                    }
                    $ret .= '</table>';
                    $ret .= '</div>';
                    $close_pg = false;
                }

            }

            if ($close_tr){
                // add remaining empty cells
                while($i % $data['cols']){
                    $ret .= '<td></td>';
                    $i++;
                }
                $ret .= '</tr>';
            }

            if(!$data['paginate']){
                $ret .= '</table>';
            }elseif ($close_pg){
                $ret .= '</table>';
                $ret .= '</div>';
            }
        }else{ // format as div sequence
            $i = 0;
            $close_pg = false;
            foreach($files as $img){

                if($data['paginate'] && ($i % $data['paginate'] == 0)){
                     $ret .= '<div class="gallery_page gallery__'.$data['galid'].'" id="gallery__'.$data['galid'].'_'.(++$page).'">';
                     $close_pg = true;
                }

                $ret .= '<div>';
                $ret .= $this->_image($img,$data);
                $ret .= $this->_showname($img,$data);
                $ret .= $this->_showtitle($img,$data);
                $ret .= '</div> ';

                $i++;

                if($data['paginate'] && ($i % $data['paginate'] == 0)){
                    $ret .= '</div>';
                    $close_pg = false;
                }
            }

            if($close_pg) $ret .= '</div>';

            $ret .= '<br style="clear:both" />';
        }

        // pagination links
        $pgret = '';
        if($page){
            $pgret .= '<div class="gallery_pages">';
            for($j=1; $j<=$page; $j++){
                $pgret .= '<a href="#gallery__'.$data['galid'].'_'.$j.'" class="gallery_pgsel">'.$j.'</a> ';
            }
            $pgret .= '</div>';
        }

        return '<div class="gallery'.$align.'"'.$xalign.'>'.$pgret.$ret.'<div class="clearer"></div></div>';
    }

    /**
     * Defines how a thumbnail should look like
     */
    function _image($img,$data){
        global $ID;

        // calculate thumbnail size
        if($data['crop']){
            $w = $data['tw'];
            $h = $data['th'];
            $dim = array('w'=>$w,'h'=>$h);
        }else{
            $w = (int) $img['meta']->getField('File.Width');
            $h = (int) $img['meta']->getField('File.Height');
            $dim = array();
            if($w > $data['tw'] || $h > $data['th']){
                $ratio = $img['meta']->getResizeRatio($data['tw'],$data['th']);
                $w = floor($w * $ratio);
                $h = floor($h * $ratio);
                $dim = array('w'=>$w,'h'=>$h);
            }
        }

        //prepare img attributes
        $i             = array();
        $i['width']    = $w;
        $i['height']   = $h;
        $i['border']   = 0;
// @JEA Begin
//      $i['alt']      = $img['meta']->getField('Simple.Title');
        // alt attribute of <img>
        $i['alt']      = $data['title'];
// @JEA End
        $i['longdesc'] = trim(str_replace("\n",' ',$img['meta']->getField('Iptc.Caption')));
        if(!$i['longdesc']) unset($i['longdesc']);
        $i['class']    = 'tn';
        $iatt = buildAttributes($i);
        $src  = ml($img['id'],$dim);

        // prepare lightbox dimensions
        $w_lightbox = $img['meta']->getField('File.Width');
        $h_lightbox = $img['meta']->getField('File.Height');
        $dim_lightbox = array();
        if($w_lightbox > $data['iw'] || $h_lightbox > $data['ih']){
            $ratio = $img['meta']->getResizeRatio($data['iw'],$data['ih']);
            $w_lightbox = floor($w_lightbox * $ratio);
            $h_lightbox = floor($h_lightbox * $ratio);
            $dim_lightbox = array('w'=>$w_lightbox,'h'=>$h_lightbox);
        }

        //prepare link attributes
        $a           = array();
// @JEA Begin
//      $a['title']  = $img['meta']->getField('Simple.Title');
        // title attribute of <a>
        $a['title']  = $data['title'];
// @JEA End
        if($data['lightbox']){
            $href   = ml($img['id'],$dim_lightbox);
            $a['class'] = "lightbox JSnocheck";
            $a['rel']   = "lightbox";
        }else{
            $href   = ml($img['id'],array('id'=>$ID),$data['direct']);
        }
        $aatt = buildAttributes($a);

        // prepare output
        $ret  = '';
        $ret .= '<a href="'.$href.'" '.$aatt.'>';
        $ret .= '<img src="'.$src.'" '.$iatt.' />';
        $ret .= '</a>';
        return $ret;
    }


    /**
     * Defines how a filename + link should look
     */
    function _showname($img,$data){
        global $ID;

        if(!$data['showname'] ) { return ''; }

        //prepare link
        $lnk = ml($img['id'],array('id'=>$ID),false);

        // prepare output
        $ret  = '';
        $ret .= '<br /><a href="'.$lnk.'">';
        $ret .= hsc($img['file']);
        $ret .= '</a>';
        return $ret;
    }

    /**
     * Defines how title + link should look
     */
    function _showtitle($img,$data){
        global $ID;

        if(!$data['showtitle'] ) { return ''; }

        //prepare link
        $lnk = ml($img['id'],array('id'=>$ID),false);

        // prepare output
        $ret  = '';
        $ret .= '<br /><a href="'.$lnk.'">';
        $ret .= hsc($img['meta']->getField('Simple.Title'));
        $ret .= '</a>';
        return $ret;
    }
}

//Setup VIM: ex: et ts=4 enc=utf-8 :

Feature Suggestion: Tell the users where the images is used

Feature Suggestion:

Option to display a linklist, on which pages (of all pages namespaces or a special given namespace or a list of given namespaces ) the image is “used”, i.e. included in the page for embedded display or as link. So that the visitor can go by the linklist to the pages where the image is used. Maybe this list should be included just when I click on a picture so that it is displayed as single picture, and not on the real gallery display page ?! Rolf Hemmerling 2009-12-28 07:00

Feature Suggestion: Link to Page that has the image Name

I am currently using the great gallery plugin together with the brand new dokutouch theme. Because of this, I have all my images named after my pages (so they display nicely in the left dokutouch column). It would be nice, if the gallery plugin could link to these pages. For images that do not have a page equivalent (e.g. the dokuwiki-128.png) there could be no link. (or the direct link as a fallback) Implemented as a new parameter (“page” instead of “direct” or so) this could be highly usable!!

I experimented a little bit: Adding

 }elseif($data['page']) {
            $new_id = cleanID($data['ns'].':'.substr($a['title'], 0, -strlen(strrchr($a['title'], '.'))));
            $newpath = wikiFN($new_id);
            if ( @file_exists($newpath) ) {
              $href = "/doku.php/".$data['ns']."/".substr($a['title'], 0, -strlen(strrchr($a['title'], '.')));
            }else{
              $href   = ml($img['id'],array('id'=>$ID),$data['direct']);
            }

In line 534 of syntax.php and adding “page” in metadata.php and setting $data['page'] = false (Around line 84) gives the result I wanted. This is of course not yet foolproof. But very nice already.

The code below acts even more sophisticated: if multiple images with different numbers at the end exist, all images link to the page. E.g.: Page: Cake Images: cake, cake2, cake3. The Numbers get cut off, all images link to the “Cake” page.

        }elseif($data['page']) {
            $sitename = substr($a['title'], 0, -strlen(strrchr($a['title'], '.')));
            $new_id = cleanID($data['ns'].':'.$sitename);
            $newpath = wikiFN($new_id);
            if ( @file_exists($newpath) ) {
              $href = "/doku.php/".$data['ns']."/".$sitename;
            }else{
              while (is_numeric(substr($sitename, -1))) {  //Remove all trailing numbers.
                $sitename = substr($sitename, 0, -1);  
              }
              $new_id = cleanID($data['ns'].':'.$sitename);
              $newpath = wikiFN($new_id);
              if ( @file_exists($newpath) ) {
                $href = "/doku.php/".$data['ns']."/".$sitename; 
              }else {
                $href   = ml($img['id'],array('id'=>$ID),$data['direct']);
              }
            }

Feature Suggestion: Images not as <img> but as background

Setting the <img> as background has the advantage of easier styling. for instance, “border-radius” can work. Other things (like css3 transformations of reflections” do also work (just a little bit) better on backgrounds.

#Line 562
        // prepare output
        $ret  = '';
        $ret .= '<center>';
        $ret .= '<a href="'.$href.'" '.$aatt.'>';
        $ret .= '<div style="width:'.$w.'px; height:'.$h.'px; background-image: url('.$src.');" '.$iatt.'></div>';
        $ret .= '</a>';
        $ret .= '</center>';
        return $ret;

The nice thing is, that the <center> tags do have NO EFFECT !! So the images are still displayed on the left of each <td> (thanks to the CSS float: left of the original plugin). But if this behaviour is not intended a “float:none” in the userstyles.css can fix it. :-)

1) This feature is based on the Lightbox and Lightbox Plus scripts with some additions
 
plugin/gallery.txt · Last modified: 2010/02/05 19:44 by 74.93.99.97
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Imprint Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki
WikiForumIRCBugsGitXRefTranslate