self::ENGINE_CSS_TO_INLINE, 'cleanup' => true, 'useInlineStylesBlock' => true, 'xhtmlOutput' => false, 'removeCss' => true, 'debug' => false, ); public $settings = array(); /** * startup */ public function __construct($settings = array()) { $defaults = am($this->_defaults, (array) Configure::read('InlineCss')); $this->settings = array_merge($defaults, $settings); if (!method_exists($this, '_process' . ucfirst($this->settings['engine']))) { throw new InternalErrorException('Engine does not exist'); } } /** * @return string Result */ public function process($html, $css = null) { if (($html = trim($html)) === '') { return $html; } $method = '_process' . ucfirst($this->settings['engine']); return $this->{$method}($html, $css); } /** * @return string Result */ protected function _processEmogrifier($html, $css) { $css .= $this->_extractAndRemoveCss($html); App::import('Vendor', 'Emogrifier', array('file' => 'emogrifier' . DS . 'emogrifier.php')); $Emogrifier = new Emogrifier($html, $css); return @$Emogrifier->emogrify(); } /** * Process css blocks to inline css * Also works for html snippets (without ) * * @return string HTML output */ protected function _processCssToInline($html, $css) { App::import('Vendor', 'Tools.CssToInlineStyles', array('file' => 'CssToInlineStyles' . DS . 'CssToInlineStyles.php')); //fix issue with being added $separator = '~~~~~~~~~~~~~~~~~~~~'; if (strpos($html, '') === false) { $incomplete = true; $html = $separator . $html . $separator; } $CssToInlineStyles = new CssToInlineStyles($html, $css); if ($this->settings['cleanup']) { $CssToInlineStyles->setCleanup(); } if ($this->settings['useInlineStylesBlock']) { $CssToInlineStyles->setUseInlineStylesBlock(); } if ($this->settings['removeCss']) { $CssToInlineStyles->setStripOriginalStyleTags(); } if ($this->settings['debug']) { CakeLog::write('css', $html); } $html = $CssToInlineStyles->convert($this->settings['xhtmlOutput']); if ($this->settings['removeCss']) { //$html = preg_replace('/\(.*)\<\/style\>/i', '', $html); $html = $this->stripOnly($html, array('style', 'script'), true); //CakeLog::write('css', $html); } if (!empty($incomplete)) { $html = substr($html, strpos($html, $separator) + 20); $html = substr($html, 0, strpos($html, $separator)); $html = trim($html); } return $html; } /** * Some reverse function of strip_tags with blacklisting instead of whitelisting * //maybe move to Tools.Utility/String/Text? * * @return string cleanedStr */ public function stripOnly($str, $tags, $stripContent = false) { $content = ''; if (!is_array($tags)) { $tags = (strpos($str, '>') !== false ? explode('>', str_replace('<', '', $tags)) : array($tags)); if (end($tags) === '') { array_pop($tags); } } foreach ($tags as $tag) { if ($stripContent) { $content = '(.+]*>|)'; } $str = preg_replace('#]*>' . $content . '#is', '', $str); } return $str; } /** * _extractAndRemoveCss - extracts any CSS from the rendered view and * removes it from the $html * * @return string */ protected function _extractAndRemoveCss($html) { $css = null; $DOM = new DOMDocument; $DOM->loadHTML($html); // DOM removal queue $removeDoms = array(); // catch style sheet content $links = $DOM->getElementsByTagName('link'); foreach ($links as $link) { if ($link->hasAttribute('href') && preg_match("/\.css$/i", $link->getAttribute('href'))) { // find the css file and load contents if ($link->hasAttribute('media')) { foreach ($this->mediaTypes as $cssLinkMedia) { if (strstr($link->getAttribute('media'), $cssLinkMedia)) { $css .= $this->_findAndLoadCssFile($link->getAttribute('href')) . "\n\n"; $removeDoms[] = $link; } } } else { $css .= $this->_findAndLoadCssFile($link->getAttribute('href')) . "\n\n"; $removeDoms[] = $link; } } } // Catch embeded