# Copyright (C) 2004, 2012 Alex Schroeder # Copyright (C) 2005 Rob Neild # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . # Thumbnail (and improved image handling) module for OddMuse wiki # Conflicts with the "Image extension module" use strict; use v5.10; require MIME::Base64; use File::Path; AddModuleDescription('thumbs.pl', 'Image Thumbnails'); our ($q, $OpenPageName, %IndexHash, @UploadTypes, @MyRules, $FreeLinkPattern); # Tempoary directory to create thumbnails in our $ThumbnailTempDir = '/tmp'; # Path and name of external program to use to create thumbnails. Only # ImageMagick 'convert' can be used. You may have to set the MAGICK_HOME # environment variable in your config file if you set it to # /usr/local/bin/convert and get the following error: # convert: no decode delegate for this image format # For your config file: # $ENV{MAGICK_HOME} = '/usr/local'; our $ThumbnailConvert = '/usr/bin/convert'; # Max size for a thumbnail. If larger size is specified just shows # regular image our $ThumbnailMaxSize = 500; # Default thumbnail size if non is specified our $ThumbnailDefaultSize = 100; # MIME types to create thumbnail for, all allowed if empty list our @ThumbnailTypes = @UploadTypes; # As well as using ALT, use TITLE. This enables comments to popup when # hovering mouse over thumbnail our $ThumbnailImageUseTitle = 0; our $ThumbnailCacheDir = "oddmuse_thumbnail_cache"; our $ThumbnailCacheUrl = "/oddmuse_thumbnail_cache"; # Define new formatting rule "thumb" that inserts an auto generated thumbnail # Syntax is [[thumb:page name | etc. ]] push(@MyRules, \&ThumbNailSupportRule); sub ThumbNailSupportRule { my $result; my $RawMatch; if (m!\G(\[\[thumb:$FreeLinkPattern(\|.*?)?\]\])!cg) { $RawMatch = $1; # Try and extract out all the options. They can be in any order, apart from comment at end my $name = $2; my $size="$ThumbnailDefaultSize"; # default size for thumbnail my $frame; my $comment; # default alignment for a non framed picture my $alignment_framed = 'tright'; # default alignment for a framed picture my $alignment; my $params = $3 . '|'; if($params =~ s/\|([0-9]+)px\|/\|/) { $size = $1; } if($params =~ s/\|thumb\|/\|/) { $frame = 'yes' ;} if($params =~ s/\|frame\|/\|/) { $frame = 'yes'; } if ($params =~ s/\|none\|/\|/) { $alignment_framed= 'tnone'; } if ($params =~ s/\|right\|/\|/) { $alignment_framed= 'tright'; $alignment='floatright';} if ($params =~ s/\|left\|/\|/) { $alignment_framed= 'tleft'; $alignment='floatleft'; } if ($params =~ m/\|(.+)\|$/) { $comment = $1; } my $id = FreeToNormal($name); AllPagesList(); # if the page does exists if ($IndexHash{$id}) { if (!IsFile("$ThumbnailCacheDir/$id/$size")) { GenerateThumbNail ($id, $size); } my %img_attribs; my $action = "$ThumbnailCacheUrl/" . UrlEncode($id) . "/$size"; $img_attribs{'-src'} = $action; if (defined $comment) { $img_attribs{'-alt'} ="$comment"; $img_attribs{'-title'} = "$comment" if $ThumbnailImageUseTitle==1; } else { $img_attribs{'-alt'} = "$name"; } $img_attribs{'-class'} = 'upload'; $result = $q->img(\%img_attribs); $result = ScriptLink(UrlEncode($id) , $result, 'image'); if (defined $frame) { if (defined $comment) { $result = $result . $q->div({-class=>'thumbcaption'}, "$comment"); } if ($size>0) { $result = $q->div({-style=>"width:" . ($size+2) . "px"}, $result); $result = $q->div({-class=>"thumb " . $alignment_framed}, $result); } } else { if (defined $alignment) { $result = $q->div({-class=>"$alignment" }, $result); } } } else { # if the image does not exist $result = '[' . T('thumb') . ':' . $name . GetEditLink($id, '?', 1) . ']'; } } if (defined $result) { Dirty($RawMatch); print $result; $result = ''; } return $result; } # define new action "thumbnail" that actually does the on fly generation of the image # thumbnails are put into the file so they only need be generated once # we also store the size of thumbnail so that can be used in the markup # if we get passed a size of zero then all we need to do is check whether we have the image size stored in thumbnail_0 # this enbles markup for non-thumbnail images better sub GenerateThumbNail { my ($id, $size) = (@_); ValidIdOrDie($id); AllPagesList(); if (not $IndexHash{$id}) { ReportError(Ts('Error creating thumbnail from nonexisting page %s.' , $id), '500 INTERNAL SERVER ERROR'); } # Page Doesn't exist, my $openpage = $OpenPageName; # remember the current page we are on RequestLockOrError(); OpenPage($id); # Parse out some data # Check MIME type supported # Check is a file my $text = GetTextRevision(GetParam('revision', ''))->{text}; # maybe revision reset! my ($type) = TextIsFile($text); # MIME type if an uploaded file my $data = substr($text, index($text, "\n") + 1); if ($type) { my $regexp = quotemeta($type); if (@ThumbnailTypes and not grep(/^$regexp$/, @ThumbnailTypes)) { ReportError(Ts('Can not create thumbnail for file type %s.' , $type), '415 UNSUPPORTED MEDIA TYPE'); } } else { ReportError(T('Can not create thumbnail for a text document'), '500 INTERNAL SERVER ERROR'); } my $filename = $ThumbnailTempDir . "/odd" . $id . "_" . $size; # Decode the original image to a temp file open(my $FD, '>', encode_utf8($filename)) or ReportError(Ts("Could not open %s for writing whilst trying to save image before creating thumbnail. Check write permissions.",$filename), '500 INTERNAL SERVER ERROR'); binmode($FD); print $FD MIME::Base64::decode($data); close($FD); eval { mkpath("$ThumbnailCacheDir/$id") }; if ($@) { ReportError(Ts('Can not create path for thumbnail - %s', $@), '500 INTERNAL SERVER ERROR'); } # create the thumbnail my $command = "$ThumbnailConvert '$filename' -verbose -resize ${size}x '$ThumbnailCacheDir/$id/$size' 2>&1"; open (my $MESSAGE, '-|', $command) or ReportError(Tss("Failed to run %1 to create thumbnail: %2", $ThumbnailConvert, $!), '500 INTERNAL SERVER ERROR'); my $convert = <$MESSAGE>; close($MESSAGE); my $scaled_size_x; my $scaled_size_y; my $thumbnail_data= ''; if($?) { ReportError(Ts("%s ran into an error", $ThumbnailConvert), '500 INTERNAL SERVER ERROR', undef, $q->pre($command . "\n" . $convert)); } elsif($convert =~ m/=>(\d+)x(\d+)/) { $scaled_size_x = $1; $scaled_size_y = $2; } elsif (!$convert) { ReportError(Ts("%s produced no output", $ThumbnailConvert), '500 INTERNAL SERVER ERROR'); } else { ReportError(Ts("Failed to parse %s.", $convert), '500 INTERNAL SERVER ERROR'); } Unlink($filename); # save tag to page #$Page{'thumbnail_' . $size} = '#FILE ' . $type . ' created=' . $Now . ' revision=' . $Page{'revision'} . ' size=' . $scaled_size_x . 'x' . $scaled_size_y . "\n" . $thumbnail_data; #SavePage(); ReleaseLock(); OpenPage($openpage); # restore original open page }