From d7e11ed7ef3b109d7407781fead0031cc7f431b1 Mon Sep 17 00:00:00 2001
From: Roy Spliet <r.spliet@student.tudelft.nl>
Date: Tue, 25 Oct 2011 23:38:12 +0200
Subject: [PATCH] Drupal/Filetree: Add thumbnail support (v2)

Signed-off-by: Roy Spliet <r.spliet@student.tudelft.nl>
---
 filetree.css       |   11 +++
 filetree.js        |   39 ++++++++++-
 filetree.module    |  205 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 images/nothumb.png |  Bin 0 -> 2669 bytes
 4 files changed, 251 insertions(+), 4 deletions(-)
 create mode 100644 images/nothumb.png

diff --git a/filetree.css b/filetree.css
index 0e829dd..f0f5c55 100644
--- a/filetree.css
+++ b/filetree.css
@@ -122,3 +122,14 @@
 .filetree li.zip {
   background-image: url(images/zip.png);
 }
+
+#filetree-thumb {
+	width: 110px;
+	height: 105px;
+	border-style: solid;
+	border-width: 1px;
+	border-color: #a0a0a0;
+	background-color: #FFFFC0;
+	text-align: center;
+	padding-top: 5px;
+}
diff --git a/filetree.js b/filetree.js
index 31398a8..f5aaec8 100644
--- a/filetree.js
+++ b/filetree.js
@@ -44,4 +44,41 @@ Drupal.behaviors.filetree = {
 
 };
 
-})(jQuery);
\ No newline at end of file
+})(jQuery);
+
+function thumb_show(url,elem) {
+	// First create the div
+	var thumb = document.createElement('div');
+	thumb.style.position = 'absolute';
+	thumb.style.display='block';
+	thumb.id = 'filetree-thumb';
+
+	// Set it's position
+	var pos = thumb_pos(elem);
+	thumb.style.left = (pos[0] + elem.offsetWidth + 5) + 'px';
+	thumb.style.top = (pos[1] + 3) +'px';
+
+	thumb.innerHTML = "<img src=\"?q="+url+"\" />";
+
+	document.body.appendChild(thumb);
+}
+
+function thumb_hide() {
+	var thumb = document.getElementById("filetree-thumb");
+	thumb.parentNode.removeChild(thumb);
+}
+
+function thumb_pos(elem) {
+	var pos = new Array();
+
+	if(elem == null || elem.tagName == "HTML") {
+		pos[0] = 0;
+		pos[1] = 0;
+		return pos;
+	} else {
+		var P = thumb_pos(elem.offsetParent);
+		pos[0] = elem.offsetLeft + P[0];
+		pos[1] = elem.offsetTop + P[1];
+		return pos;
+	}
+}
diff --git a/filetree.module b/filetree.module
index ceed201..b19e872 100755
--- a/filetree.module
+++ b/filetree.module
@@ -76,8 +76,8 @@ function _filetree_tips($filter, $format, $long = FALSE) {
   $output = t('You may use [filetree dir="some-directory"] to display a list of files inline.');
   if ($long) {
     $output = '<p>' . $output . '</p>';
-    $output .= '<p>' . t('Additional options include "multi", "controls", "absolute", "exclude", "dirname", "dirtitle", "filename", and "filetitle". For example:') . '</p>';
-    $output .= '<blockquote>[filetree dir="some-directory" multi="false" controls="false" absolute="false" exclude="CVS; directory1; directory2" dirname="%basename" dirtitle="Click to toggle this folder." filename="%basename" filetitle="Click to download this %extension file."]</blockquote>';
+    $output .= '<p>' . t('Additional options include "multi", "controls", "absolute", "exclude", "dirname", "dirtitle", "thumbs", "filename", and "filetitle". For example:') . '</p>';
+    $output .= '<blockquote>[filetree dir="some-directory" multi="false" controls="false" absolute="false" exclude="CVS; directory1; directory2" dirname="%basename" dirtitle="Click to toggle this folder." thumbs="true" filename="%basename" filetitle="Click to download this %extension file."]</blockquote>';
     $output .= '<p>' . t('Available tokens for use within the "dirname", "dirtitle", "filename", and "filetitle" options include:') . '</p>';
     $output .= theme('item_list', array('items' => array(
       '%filename: ' . t("The file's name and extension."),
@@ -109,6 +109,7 @@ function _filetree_process($text, $filter, $format, $langcode, $cache, $cache_id
     'dirtitle' => '%filename',
     'filename' => '%filename',
     'filetitle' => '%filename',
+    'thumbs' => FALSE
   );
 
   // The token might be present multiple times; loop through each instance.
@@ -196,8 +197,16 @@ function _filetree_list_files($dir, $params) {
         else {
           $name = isset($descriptions[$file]) ? $descriptions[$file] : token_replace($params['filename'], array('file' => $file));
           $url = $params['absolute'] ? file_create_url($filename) : substr(file_create_url($filename), strlen($GLOBALS['base_url'] . '/'));
+	  if($params['thumbs'] && _filetree_image(pathinfo($file, PATHINFO_EXTENSION))) {
+		$thumb = file_create_url($filename);
+		$thumb = substr(strstr($thumb,'system/files'),12);
+		$data = '<a href="'.url(urldecode($url)).'" onmouseover="javascript:thumb_show(\'filetree/thumb'.$thumb.'\',this)" onmouseout="javascript:thumb_hide()">'.$name.'</a>';
+	} else {
+		$data = l($name,urldecode($url));
+	}
           $files[$file] = array(
-            'data' => l($name, urldecode($url)),
+            /*'data' => l($name, urldecode($url)),*/
+	    'data' => $data,
             'title' => token_replace($params['filetitle'], array('file' => $file)),
             'class' => array(_filetree_icon(pathinfo($file, PATHINFO_EXTENSION))),
           );
@@ -284,6 +293,31 @@ function _filetree_icon($extension) {
   return $icon;
 }
 
+function _filetree_image($extension) {
+	$extension = strtolower($extension);
+	if(!extension_loaded('gd')) {
+		return false;
+	}
+
+	switch($extension) {
+	case 'jpg':
+	case 'jpeg':
+		return imagetypes() & IMG_JPG;
+		break;
+	case 'png':
+		return imagetypes() & IMG_PNG;
+		break;
+	case 'bmp':
+		return imagetypes() & IMG_WBMP;
+		break;
+	case 'gif':
+		return imagetypes() & IMG_GIF;
+		break;
+	}
+
+	return false;
+}
+
 /**
  * Renders filetree.
  */
@@ -365,9 +399,174 @@ function filetree_menu() {
 		'type' => MENU_NORMAL_ITEM
 	);
 
+	$items['filetree/thumb'] = array(
+		'title' => 'thumbnail',
+		'page callback' => 'filetree_get_thumb',
+		'access arguments' => array('access content'),
+		'type' => MENU_CALLBACK
+	);
+
 	return $items;
 }
 
+/**
+ * Fetch the right thumbnail based on the path
+ */
+function filetree_get_thumb() {
+	/* Suppress all output, we're giving away images today */
+	module_invoke('admin_menu', 'suppress');
+	ini_set('display_errors','0');
+
+	$file = pathinfo(substr(strstr($_REQUEST['q'],'filetree/thumb'),14,strlen($_REQUEST['q'])));
+	$file['dir_uri'] = file_build_uri($file['dirname']);
+	$file['file_uri'] = file_build_uri($file['dirname'].'/'.$file['basename']);
+
+	/* Check if the original file exists at all */
+	if(!file_exists($file['dir_uri']) ||
+	!is_dir($file['dir_uri']) ||
+	!file_exists($file['file_uri']) ||
+	file_prepare_directory($file['file_uri'],0)) {
+		filetree_exit_nothumb_error();
+	}
+
+	/* Create thumbnail directory if it doesn't already exist */
+	if(strtolower(substr(PHP_OS,0,7)) == "windows") {
+		$thumb_dirname = '/_filetree_thumbs';
+	} else {
+		$thumb_dirname = '/.filetree_thumbs';
+	}
+	$file['dir_uri'] .= $thumb_dirname;
+	$file['thumb_uri'] = file_build_uri($file['dirname'].$thumb_dirname.'/'.$file['basename']);
+
+	/* Remove thumbnail if it's old */
+	if(file_exists($file['thumb_uri']) && filemtime($file['file_uri']) > filemtime($file['thumb_uri'])) {
+		/* Force write permission */
+		_filetree_ensure_thumb_dir($file['dir_uri']);
+
+		if(!unlink($file['thumb_uri'])) {
+			$error = t("Could not delete old thumbnail '@path': file not writeable", array('@path' => $file['thumb_uri']));
+			filetree_exit_nothumb_error($error);
+		}
+	}
+
+	/* Create the thumbnail if it doesn't exist yet */
+	if(!file_exists($file['thumb_uri'])) {
+		if(!_filetree_create_thumb($file['file_uri'],$thumb_dirname)) {
+			$error = t("Could not create thumbnail '@path': folder not writeable", array('@path' => $file['thumb_uri']));
+			filetree_exit_nothumb_error($error);
+		}
+	}
+
+	/* If the thumb exists, serve it */
+	if(file_exists($file['thumb_uri']) && !file_prepare_directory($file['thumb_uri'],0) && is_readable($file['thumb_uri'])) {
+		$headers = array(
+			'Content-Type' => file_get_mimetype($file['thumb_uri']),
+			'Content-Length' => filesize($file['thumb_uri'])
+		);
+		file_transfer($file['thumb_uri'],$headers);
+		exit();
+	} else {
+		$error = t("Could not open thumbnail '@path': Permission error", array('@path' => $file['thumb_uri']));
+		filetree_exit_nothumb_error($error);
+	}
+}
+
+function _filetree_ensure_thumb_dir($dir_uri) {
+	if(!file_prepare_directory($dir_uri,FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
+		$error = t("Could not create thumbnail directory '@path': folder not writeable",array('@path' => $dir_uri));
+		filetree_exit_nothumb_error($error);
+	} elseif(strtolower(substr(PHP_OS,0,7)) == "windows") {
+		system("attrib +h ".shellescapearg(realpath($dir_uri)));
+	}
+}
+
+function _filetree_create_thumb($url,$thumb_dir) {
+	/* Get extension */
+	$file = pathinfo($url);
+	$dest = $file['dirname'].$thumb_dir.'/'.$file['basename'];
+	_filetree_ensure_thumb_dir($dest);
+	$image;
+
+	/* Load original file */
+	switch(strtolower($file['extension'])) {
+	case 'jpg':
+	case 'jpeg':
+		$image = imagecreatefromjpeg($url);
+		break;
+	case 'png':
+		$image = imagecreatefrompng($url);
+		break;
+	case 'bmp':
+		$image = imagecreatefromwbmp($url);
+		break;
+	case 'gif':
+		$image = imagecreatefromgif($url);
+		break;
+	default:
+		$image = false;
+	}
+
+	if(!$image) return false;
+
+	/* Scale */
+	$sx = imagesx($image);
+	$sy = imagesy($image);
+
+	if($sx > 100 || $sy > 100) {
+		if($sx > $sy) {
+			// Scale to x=100;
+			$newsx = 100;
+			$newsy = ($sy*100)/$sx;
+		} else {
+			$newsx = ($sx*100)/$sy;
+			$newsy = 100;
+		}
+		$thumb = imagecreatetruecolor($newsx,$newsy);
+
+		imagecopyresized($thumb,$image,0,0,0,0,$newsx,$newsy,$sx,$sy);
+		imagedestroy($image);
+	} else {
+		$thumb = $image;
+	}
+
+	/* Store thumbnail */
+	switch(strtolower($file['extension'])) {
+	case 'jpg':
+	case 'jpeg':
+		$ret = imagejpeg($thumb,drupal_realpath($dest));
+		break;
+	case 'png':
+		$ret = imagepng($thumb,$dest);
+		break;
+	case 'bmp':
+		$ret = image2wbmp($thumb,$dest);
+		break;
+	case 'gif':
+		$ret = imagegif($thumb,$dest);
+		break;
+	default:
+		$ret = false;
+	}
+
+	imagedestroy($thumb);
+	return $ret;
+}
+
+/**
+ * Show the "nothumb.png" file and log an error message in watchdog.
+ * @param $pError the errormessage to be logged
+ * @return never. The method calls exit();
+ */
+function filetree_exit_nothumb_error($pError = '') {
+	if($pError != '') {
+		watchdog('filetree',$pError,null,WATCHDOG_WARNING);
+	}
+	header("Content-type: image/png");
+	module_load_include('png','filetree','images/nothumb');
+
+	exit();
+}
+
 function filetree_admin_form($form,&$form_state) {
 	$form = array();
 	$form['filetree_private_download_mgr'] = array(
diff --git a/images/nothumb.png b/images/nothumb.png
new file mode 100644
index 0000000000000000000000000000000000000000..b603ff631647e00632930c04dc85335c9540c8d4
GIT binary patch
literal 2669
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
F@!x~=?d1Rf

literal 0
HcmV?d00001

-- 
1.7.6.4

