From 03149131f754dd122f8707fbfc9e7ff47e9d6524 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Sat, 10 Nov 2012 15:08:14 -0500
Subject: [PATCH] New feature: display attached images as thumbnails below message body

---
 program/include/html.php               |    4 
 skins/classic/mail.css                 |   59 ++++++++++++++
 program/include/rcube_image.php        |   23 ++++-
 program/steps/mail/func.inc            |   49 ++++++++++-
 skins/larry/templates/messagepart.html |    2 
 program/steps/mail/get.inc             |   36 +++++++++
 config/main.inc.php.dist               |    5 +
 skins/larry/mail.css                   |   43 ++++++++++
 8 files changed, 204 insertions(+), 17 deletions(-)

diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index 35fe0e8..dc76f18 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -373,6 +373,11 @@
 // path to imagemagick convert binary
 $rcmail_config['im_convert_path'] = null;
 
+// Size of thumbnails from image attachments displayed below the message content.
+// Note: whether images are displayed at all depends on the 'inline_images' option.
+// Set to 0 to display images in full size.
+$rcmail_config['image_thumbnail_size'] = 240;
+
 // maximum size of uploaded contact photos in pixel
 $rcmail_config['contact_photo_size'] = 160;
 
diff --git a/program/include/html.php b/program/include/html.php
index 880873d..0f93e96 100644
--- a/program/include/html.php
+++ b/program/include/html.php
@@ -252,9 +252,9 @@
      * @return string HTML code
      * @see html::tag()
      */
-    public static function br()
+    public static function br($attrib = array())
     {
-        return self::tag('br');
+        return self::tag('br', $attrib);
     }
 
     /**
diff --git a/program/include/rcube_image.php b/program/include/rcube_image.php
index 80e8bd4..c0d4e87 100644
--- a/program/include/rcube_image.php
+++ b/program/include/rcube_image.php
@@ -78,10 +78,11 @@
      *
      * @param int    $size      Max width/height size
      * @param string $filename  Output filename
+     * @param boolean $browser_compat  Convert to image type displayable by any browser
      *
-     * @return bool True on success, False on failure
+     * @return mixed Output type on success, False on failure
      */
-    public function resize($size, $filename = null)
+    public function resize($size, $filename = null, $browser_compat = false)
     {
         $result  = false;
         $rcube   = rcube::get_instance();
@@ -104,15 +105,22 @@
             }
 
             $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));
+            $p['intype'] = $type;
+
+            // convert to an image format every browser can display
+            if ($browser_compat && !in_array($type, array('jpg','gif','png'))) {
+                $type = 'jpg';
+            }
+
             $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
-            $p['-opts'] = array('-resize' => $size.'>');
+            $p['-opts'] = array('-resize' => $p['size'].'>');
 
             if (in_array($type, explode(',', $p['types']))) { // Valid type?
-                $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p);
+                $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
             }
 
             if ($result === '') {
-                return true;
+                return $type;
             }
         }
 
@@ -148,16 +156,19 @@
 
             if ($props['gd_type'] == IMAGETYPE_JPEG) {
                 $result = imagejpeg($image, $filename, 75);
+                $type = 'jpg';
             }
             elseif($props['gd_type'] == IMAGETYPE_GIF) {
                 $result = imagegif($image, $filename);
+                $type = 'gid';
             }
             elseif($props['gd_type'] == IMAGETYPE_PNG) {
                 $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS);
+                $type = 'png';
             }
 
             if ($result) {
-                return true;
+                return $type;
             }
         }
 
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index f128a38..c0d36da 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1186,7 +1186,9 @@
   }
 
   // list images after mail body
-  if ($CONFIG['inline_images'] && !empty($MESSAGE->attachments)) {
+  if ($RCMAIL->config->get('inline_images', true) && !empty($MESSAGE->attachments)) {
+    $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240);
+
     foreach ($MESSAGE->attachments as $attach_prop) {
       // skip inline images
       if ($attach_prop->content_id && $attach_prop->disposition == 'inline') {
@@ -1195,12 +1197,45 @@
 
       // Content-Type: image/*...
       if (rcmail_part_image_type($attach_prop)) {
-        $out .= html::tag('hr') . html::p(array('align' => "center"),
-          html::img(array(
-            'src' => $MESSAGE->get_part_url($attach_prop->mime_id, true),
-            'title' => $attach_prop->filename,
-            'alt' => $attach_prop->filename,
-          )));
+        // display thumbnails
+        if ($thumbnail_size) {
+          $show_link = array(
+            'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
+            'onclick' => sprintf(
+              'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
+              JS_OBJECT_NAME,
+              $attach_prop->mime_id,
+              rcmail_fix_mimetype($attach_prop->mimetype))
+          );
+          $out .= html::p('image-attachment',
+             html::a($show_link + array('class' => 'image-link'),
+               html::img(array(
+                'class' => 'image-thumbnail',
+                'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, true) . '&_thumb=1',
+                'title' => $attach_prop->filename,
+                'alt'   => $attach_prop->filename,
+                'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size),
+              ))
+            ) .
+            html::span('image-filename', Q($attach_prop->filename)) .
+            html::span('image-filesize', Q($RCMAIL->show_bytes($attach_prop->size))) .
+            html::span('attachment-links',
+              html::a($show_link['href'] . '&_download=1', rcube_label('download'))
+            ) .
+            html::br(array('style' => 'clear:both'))
+          );
+        }
+        else {
+          $out .= html::tag('fieldset', 'image-attachment',
+            html::tag('legend', 'image-filename', Q($attach_prop->filename)) .
+            html::p(array('align' => "center"),
+              html::img(array(
+                'src'   => $MESSAGE->get_part_url($attach_prop->mime_id, true),
+                'title' => $attach_prop->filename,
+                'alt'   => $attach_prop->filename,
+              )))
+          );
+        }
       }
     }
   }
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index bcd57de..2397358 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -60,6 +60,42 @@
   exit;
 }
 
+// render thumbnail of an image attachment
+else if ($_GET['_thumb']) {
+  $pid = get_input_value('_part', RCUBE_INPUT_GET);
+  if ($part = $MESSAGE->mime_parts[$pid]) {
+    $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240);
+    $temp_dir = $RCMAIL->config->get('temp_dir');
+    list(,$ext) = explode('/', $part->mimetype);
+    $cache_basename = $temp_dir . '/' . md5($MESSAGE->headers->messageID . $part->mime_id . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size);
+    $cache_file = $cache_basename . '.' . $ext;
+    $mimetype = $part->mimetype;
+
+    // render thumbnail image if not done yet
+    if (!is_file($cache_file)) {
+      $fp = fopen(($orig_name = $cache_basename . '.orig.' . $ext), 'w');
+      $MESSAGE->get_part_content($part->mime_id, $fp);
+      fclose($fp);
+
+      $image = new rcube_image($orig_name);
+      if ($imgtype = $image->resize($RCMAIL->config->get('image_thumbnail_size', 240), $cache_file, true)) {
+        $mimetype = 'image/' . $imgtype;
+        unlink($orig_name);
+      }
+      else {
+        rename($orig_name, $cache_file);
+      }
+    }
+
+    if (is_file($cache_file)) {
+      header('Content-Type: ' . $mimetype);
+      readfile($cache_file);
+    }
+  }
+
+  exit;
+}
+
 else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
 
   if ($part = $MESSAGE->mime_parts[$pid]) {
diff --git a/skins/classic/mail.css b/skins/classic/mail.css
index 7408d49..85c53d5 100644
--- a/skins/classic/mail.css
+++ b/skins/classic/mail.css
@@ -1254,6 +1254,65 @@
   color: #333333;
 }
 
+#messagebody fieldset.image-attachment {
+  border: 0;
+  border-top: 1px solid #ccc;
+  margin: 1em 1em 0 1em;
+}
+
+#messagebody fieldset.image-attachment p > img
+{
+  max-width: 80%;
+}
+
+#messagebody legend.image-filename
+{
+  color: #999;
+  font-size: 0.9em;
+}
+
+#messagebody p.image-attachment
+{
+  margin: 0 1em;
+  padding: 1em;
+  border-top: 1px solid #ccc;
+}
+
+#messagebody p.image-attachment a.image-link
+{
+  float: left;
+  margin-right: 2em;
+  min-width: 160px;
+  min-height: 60px;
+  text-align: center;
+}
+
+#messagebody p.image-attachment .image-filename
+{
+  display: block;
+  font-weight: bold;
+  line-height: 1.6em;
+}
+
+#messagebody p.image-attachment .image-filesize
+{
+  font-size: 11px;
+  padding-right: 1em;
+}
+
+#messagebody p.image-attachment .attachment-links a
+{
+  margin-right: 0.6em;
+  color: #cc0000;
+  font-size: 11px;
+  text-decoration: none;
+}
+
+#messagebody p.image-attachment .attachment-links a:hover
+{
+  text-decoration: underline;
+}
+
 #openextwinlink
 {
   position: absolute;
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index eb62322..48560ab 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -1050,10 +1050,51 @@
 	border-bottom: 2px solid #f0f0f0;
 }
 
-#messagebody > p > img {
+#messagebody fieldset.image-attachment {
+	border: 0;
+	border-top: 1px solid #ccc;
+	margin-top: 1em;
+}
+
+#messagebody fieldset.image-attachment p > img {
 	max-width: 80%;
 }
 
+#messagebody legend.image-filename {
+	color: #999;
+	font-size: 0.9em;
+	margin: 0 1em;
+}
+
+#messagebody p.image-attachment {
+	position: relative;
+	padding: 1em;
+	border-top: 1px solid #ccc;
+}
+
+#messagebody p.image-attachment a.image-link {
+	float: left;
+	display: block;
+	margin-right: 2em;
+	min-width: 160px;
+	min-height: 60px;
+	text-align: center;
+}
+
+#messagebody p.image-attachment .image-filename {
+	display: block;
+	font-weight: bold;
+	line-height: 1.6em;
+}
+
+#messagebody p.image-attachment .image-filesize {
+	padding-right: 1em;
+}
+
+#messagebody p.image-attachment .attachment-links a {
+	margin-right: 0.6em;
+}
+
 #messagepartcontainer {
 	position: absolute;
 	top: 60px;
diff --git a/skins/larry/templates/messagepart.html b/skins/larry/templates/messagepart.html
index db07829..e029973 100644
--- a/skins/larry/templates/messagepart.html
+++ b/skins/larry/templates/messagepart.html
@@ -4,7 +4,7 @@
 <title><roundcube:object name="pagetitle" /></title>
 <roundcube:include file="/includes/links.html" />
 </head>
-<body class="extwin">
+<body class="partwin">
 
 <div id="header">
 <div id="topline">

--
Gitblit v1.9.1