Ticket #8592 - More IFRAME/HTML work
authorMichael M Slusarz <slusarz@curecanti.org>
Mon, 28 Sep 2009 03:12:59 +0000 (21:12 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Mon, 28 Sep 2009 03:52:28 +0000 (21:52 -0600)
IFRAME & IFRAME data are now loaded on the same page; javascript does
the dynamic addition of the text to the IFRAME.  Saves a server access.
Image block display now works again entirely via javascript, rather than
requiring another server access.

imp/js/imp.js
imp/lib/Contents.php
imp/lib/Mime/Viewer/Html.php
imp/lib/Views/ShowMessage.php
imp/message-dimp.php
imp/message.php
imp/themes/screen.css

index 0fb08f2..9843cf5 100644 (file)
@@ -26,9 +26,34 @@ document.observe('dom:loaded', function() {
      */
     IMP.unblockImages = function(e)
     {
-        var elt = e.element().up('TABLE.mimeStatusMessage');
+        var elt = e.element().up('TABLE.mimeStatusMessage'),
+            iframe = elt.up().next('.htmlMsgData'),
+            s = new Selector('[htmlimgblocked]');
 
-        elt.up().next('.htmlMessage').writeAttribute('src', e.element().readAttribute('href'));
+        // Need to use non-prototypejs methods to work with data inside of
+        // the IFRAME. Prototypejs's Selector works, but only if we use
+        // the pure javascript method.
+        if (s.mode != 'normal') {
+            delete Selector._cache['[htmlimgblocked]'];
+            s.mode = 'normal';
+            s.compileMatcher();
+        }
+
+        s.findElements(iframe.contentWindow.document).each(function(b) {
+            var src = decodeURIComponent(b.getAttribute('htmlimgblocked'));
+            if (b.getAttribute('src')) {
+                b.setAttribute('src', src);
+            } else if (b.getAttribute('background')) {
+                b.setAttribute('background', src);
+            } else if (b.style.backgroundImage) {
+                b.style.setProperty('background-image', 'url(' + src + ')', '');
+            }
+        });
+
+        // Delete this entry, because in the rare case that another selector
+        // on the page uses the same expression, it will break the next time
+        // it is used.
+        delete Selector._cache['[htmlimgblocked]'];
 
         Effect.Fade(elt, {
             afterFinish: function() { elt.remove(); },
@@ -38,6 +63,24 @@ document.observe('dom:loaded', function() {
         e.stop();
     };
 
+    IMP.iframeInject = function(id, data)
+    {
+        id = $(id);
+        var d = id.contentWindow.document;
+
+        d.open();
+        d.write(data);
+        d.close();
+
+        this.iframeResize(id);
+    };
+
+    IMP.iframeResize = function(id)
+    {
+        id = $(id);
+        id.setStyle({ height: id.contentWindow.document.height + 'px' });
+    };
+
     // If menu is present, attach event handlers to folder switcher.
     var tmp = $('openfoldericon');
     if (tmp) {
index fb03741..4b7afa2 100644 (file)
@@ -373,9 +373,11 @@ class IMP_Contents
      *          identified in the MIME part.
      * </pre>
      *
-     * @return array  See Horde_Mime_Viewer_Driver::render(). Additionally,
-     *                a entry in the base array labeled 'name' will be present
-     *                which contains the MIME name information.
+     * @return array  See Horde_Mime_Viewer_Driver::render(). The following
+     *                fields may also be present:
+     *                'js' - (array) A list of javascript commands to run
+     *                       after the content is displayed on screen.
+     *                'name' - (string) Contains the MIME name information.
      */
     public function renderMIMEPart($mime_id, $mode, $options = array())
     {
index 680064e..a39acbf 100644 (file)
@@ -57,20 +57,20 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
         \s*
         # opening quotes, parenthesis; match 4
         ("|\')?
-        # the image url
-        (?(2)
+        # the image url; match 5
+        ((?(2)
             # matched a "style" attribute
             (?(4)[^"\')>]*|[^\s)>]*)
             # did not match a "style" attribute
             |(?(4)[^"\'>]*|[^\s>]*)
-        )
+        ))
         # closing quotes
         (?(4)\\4)
         # matched a "style" attribute?
         (?(2)
             # closing parenthesis
             \s*\)
-            # remainder of the "style" attribute; match 5
+            # remainder of the "style" attribute; match 6
             ((?(3)[^"\'>]*|[^\s>]*))
         )
         /isx';
@@ -90,56 +90,40 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
     /**
      * Return the rendered inline version of the Horde_Mime_Part object.
      *
-     * URL parameters used by this function:
-     * <pre>
-     * 'html_iframe_data' - (boolean) If true, output the iframe content only.
-     * 'html_view_images' - (boolean) If true, force display of images.
-     * </pre>
-     *
      * @return array  See Horde_Mime_Viewer_Driver::render().
      */
     protected function _renderInline()
     {
-        if (Horde_Util::getFormData('html_iframe_data')) {
-            $html = $this->_IMPrender(true);
-        } else {
-            $data = '';
-            $view_part = isset($this->_params['related_id'])
-                ? $this->_params['contents']->getMIMEPart($this->_params['related_id'])
-                : $this->_mimepart;
-
-            $src = $this->_params['contents']->urlView($view_part, 'view_attach', array('params' => array('html_iframe_data' => 1, 'mode' => IMP_Contents::RENDER_INLINE)));
-
-            /* Check for phishing exploits. */
-            $contents = $this->_mimepart->getContents();
-            $this->_phishingCheck($contents, true);
-            $status = array($this->_phishingStatus());
-
-            /* Only display images if specifically allowed by user. */
-            if ($GLOBALS['prefs']->getValue('html_image_replacement') &&
-                preg_match($this->_img_regex, $contents)) {
-                // Unblock javascript code in js/imp.js
-                $data = Horde_Util::bufferOutput(array('Horde', 'addScriptFile'), 'imp.js', 'imp', true);
-
-                $status[] = array(
-                    'icon' => Horde::img('mime/image.png'),
-                    'text' => array(
-                        _("Images have been blocked to protect your privacy."),
-                        Horde::link(Horde_Util::addParameter($src, 'html_view_images', 1), '', 'unblockImageLink') . _("Show Images?") . '</a>'
-                    )
-                );
-            }
-
-            $html = array(
-                // TODO: Why do we need extra 10 pixels, at least on FF 3.1?
-                'data' => '<IFRAME class="htmlMessage" src="' . $src . '" onload="this.setStyle({ height: this.contentWindow.document.height + 10 + \'px\' })" frameborder="0"></iframe>' . $data,
-                'status' => $status,
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+        /* Non-javascript browsers can't handle IFRAME resizing, so it isn't
+         * possible to view inline. */
+        if (!$GLOBALS['browser']->hasFeature('javascript')) {
+            return array(
+                $this->_mimepart->getMimeId() => array(
+                    'data' => '',
+                    'status' => array(
+                        array(
+                            'icon' => Horde::img('mime/html.png'),
+                            'text' => array(
+                                _("This message part contains HTML data, but this data can not be displayed inline."),
+                                $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View HTML data in new window.")),
+                            )
+                        )
+                    ),
+                    'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+                )
             );
         }
 
+        $data = $this->_IMPrender(true);
+        $uid = 'htmldata_' . uniqid(mt_rand());
+
+        $data['js'] = array('IMP.iframeInject("' . $uid . '", ' . Horde_Serialize::serialize($data['data'], Horde_Serialize::JSON, $this->_mimepart->getCharset()) . ')');
+        $data['data'] = '<IFRAME class="htmlMsgData" id="' . $uid . '" src="javascript:false" frameborder="0"></IFRAME>' .
+            Horde_Util::bufferOutput(array('Horde', 'addScriptFile'), 'imp.js', 'imp', true);
+        $data['type'] = 'text/html; charset=' . Horde_Nls::getCharset();
+
         return array(
-            $this->_mimepart->getMimeId() => $html
+            $this->_mimepart->getMimeId() => $data
         );
     }
 
@@ -154,19 +138,17 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
             return array();
         }
 
-        $status = array(
-            _("This message part contains HTML data, but inline HTML display is disabled."),
-            $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View HTML data in new window.")),
-            $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("Convert HTML data to plain text and view in new window."), array('params' => array('convert_text' => 1)))
-        );
-
         return array(
             $this->_mimepart->getMimeId() => array(
                 'data' => '',
                 'status' => array(
                     array(
                         'icon' => Horde::img('mime/html.png', _("HTML data")),
-                        'text' => $status
+                        'text' => array(
+                            _("This message part contains HTML data, but inline HTML display is disabled."),
+                            $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View HTML data in new window.")),
+                            $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("Convert HTML data to plain text and view in new window."), array('params' => array('convert_text' => 1)))
+                        )
                     )
                 ),
                 'type' => 'text/html; charset=' . Horde_Nls::getCharset()
@@ -184,10 +166,10 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
     protected function _IMPrender($inline)
     {
         $data = $this->_mimepart->getContents();
-        $charset = Horde_Nls::getCharset();
 
         /* Sanitize the HTML. */
         $data = $this->_cleanHTML($data, array('phishing' => $inline));
+        $status = array($this->_phishingStatus());
 
         /* We are done processing if in mimp mode, or we are converting to
          * text. */
@@ -199,7 +181,7 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
             return array(
                 'data' => IMP::filterText($data),
                 'status' => array(),
-                'type' => 'text/plain; charset=' . $charset
+                'type' => 'text/plain; charset=' . Horde_Nls::getCharset()
             );
         }
 
@@ -253,12 +235,19 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
 
         /* Image filtering. */
         if ($inline &&
-            !Horde_Util::getFormData('html_view_images') &&
             $GLOBALS['prefs']->getValue('html_image_replacement') &&
             preg_match($this->_img_regex, $this->_mimepart->getContents()) &&
             (!$GLOBALS['prefs']->getValue('html_image_addrbook') ||
              !$this->_inAddressBook())) {
             $data = preg_replace_callback($this->_img_regex, array($this, '_blockImages'), $data);
+
+            $status[] = array(
+                'icon' => Horde::img('mime/image.png'),
+                'text' => array(
+                    _("Images have been blocked to protect your privacy."),
+                    Horde::link('#', '', 'unblockImageLink') . _("Show Images?") . '</a>'
+                )
+            );
         }
 
         if ($GLOBALS['prefs']->getValue('emoticons')) {
@@ -267,7 +256,7 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
 
         return array(
             'data' => $data,
-            'status' => array(),
+            'status' => $status,
             'type' => $this->_mimepart->getType(true)
         );
     }
@@ -290,12 +279,12 @@ class IMP_Horde_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
     protected function _blockImages($matches)
     {
         if (is_null($this->_blockimg)) {
-            $this->_blockimg = Horde::url($GLOBALS['registry']->getImageDir('imp', false) . '/spacer_red.png', false, -1);
+            $this->_blockimg = Horde::url($GLOBALS['registry']->getImageDir('imp', false) . '/spacer_red.png', true, -1);
         }
 
         return empty($matches[2])
-            ? $matches[1] . '"' . $this->_blockimg . '"'
-            : $matches[1] . "'" . $this->_blockimg . '\')' . $matches[5] . '"';
+            ? $matches[1] . '"' . $this->_blockimg . '" htmlimgblocked="' . rawurlencode(str_replace('&amp;', '&', trim($matches[5], '\'" '))) . '"'
+            : $matches[1] . "'" . $this->_blockimg . '\')' . $matches[6] . '" htmlimgblocked="' . rawurlencode(str_replace('&amp;', '&', trim($matches[5], '\'" ')));
     }
 
     /**
index 7316327..ee553d3 100644 (file)
@@ -68,6 +68,7 @@ class IMP_Views_ShowMessage
      * 'errortype' - Contains the error type (only on error)
      * 'from' - The From addresses
      * 'index' - The IMAP UID
+     * 'js' - Javascript code to run on display
      * 'log' - Log information
      * 'mailbox' - The IMAP mailbox
      * 'msgtext' - The text of the message
@@ -75,8 +76,6 @@ class IMP_Views_ShowMessage
      *
      * FOR PREVIEW MODE:
      * 'fulldate' - The fully formatted date
-     * 'js' - Javascript code to run on display (only if the previewview
-     *        hook is active)
      * 'minidate' - A miniature date
      *
      * FOR NON-PREVIEW MODE:
@@ -98,6 +97,7 @@ class IMP_Views_ShowMessage
 
         $result = array(
             'index' => $index,
+            'js' => array(),
             'mailbox' => $mailbox
         );
 
@@ -296,6 +296,10 @@ class IMP_Views_ShowMessage
                 }
 
                 $result['msgtext'] .= '<div><span class="mimePartInfo">' . implode(' ', $tmp_summary) . '</span></div>' . implode("\n", $tmp_status) . $info['data'];
+
+                if (isset($info['js'])) {
+                    $result['js'] = array_merge($result['js'], $info['js']);
+                }
             }
         }
 
@@ -338,7 +342,7 @@ class IMP_Views_ShowMessage
                 $res = Horde::callHook('dimp_previewview', array($result), 'imp');
                 if (!empty($res)) {
                     $result = $res[0];
-                    $result['js'] = $res[1];
+                    $result['js'] = array_merge($result['js'], $res[1]);
                 }
             } catch (Horde_Exception_HookNotSet $e) {}
         } elseif (!$preview) {
@@ -352,6 +356,10 @@ class IMP_Views_ShowMessage
             $result['save_as'] = Horde::downloadUrl(htmlspecialchars_decode($result['subject']), array_merge(array('actionID' => 'save_message'), IMP::getIMPMboxParameters($mailbox, $index, $mailbox)));
         }
 
+        if (empty($result['js'])) {
+            unset($result['js']);
+        }
+
         return $result;
     }
 }
index 757ace4..7198af3 100644 (file)
@@ -41,8 +41,7 @@ if (isset($show_msg_result['error'])) {
 
 $scripts = array(
     array('ContextSensitive.js', 'imp', true),
-    array('fullmessage-dimp.js', 'imp', true),
-    array('imp.js', 'imp', true)
+    array('fullmessage-dimp.js', 'imp', true)
 );
 
 $js_out = array();
@@ -78,8 +77,13 @@ if (!$disable_compose) {
     Horde::addInlineScript($compose_result['jsonload'], 'load');
 }
 
+$js_onload = array(IMP_Dimp::notify());
+if (isset($show_msg_result['js'])) {
+    $js_onload = array_merge($js_onload, $show_msg_result['js']);
+}
+
 Horde::addInlineScript($js_out);
-Horde::addInlineScript(array(IMP_Dimp::notify()), 'dom');
+Horde::addInlineScript($js_onload, 'dom');
 
 IMP_Dimp::header($show_msg_result['subject'], $scripts);
 echo "<body>\n";
index e8c6535..a4bf739 100644 (file)
@@ -579,6 +579,7 @@ $part_info_action = array('download', 'download_zip', 'img_save', 'strip');
 $parts_list = $imp_contents->getContentTypeMap();
 $strip_atc = $prefs->getValue('strip_attachments');
 $atc_parts = $display_ids = array();
+$js_onload = array();
 $msgtext = '';
 
 /* Do MDN processing now. */
@@ -649,6 +650,10 @@ foreach ($parts_list as $mime_id => $mime_type) {
         $msgtext .= '<div><span class="mimePartInfo">' . implode(' ', $tmp_summary) . '</span></div>' .
             implode("\n", $tmp_status) .
             $info['data'];
+
+        if (isset($info['js'])) {
+            $js_onload = array_merge($js_onload, $info['js']);
+        }
     }
 }
 
@@ -713,6 +718,7 @@ $m_template->set('headers', $hdrs);
 $m_template->set('msgtext', $msgtext);
 
 /* Output message page now. */
+Horde::addInlineScript($js_onload, 'dom');
 Horde::addScriptFile('effects.js', 'horde', true);
 Horde::addScriptFile('imp.js', 'imp', true);
 Horde::addScriptFile('message.js', 'imp', true);
index a827c17..8415d94 100644 (file)
@@ -210,7 +210,7 @@ span.trashImg {
 }
 
 /* Style for HTML data iframe. */
-.htmlMessage {
+.htmlMsgData {
     margin: 5px 0;
     border: 0;
     width: 100%;