From 47d65931e627936e42f5ac0f3fe844116630c166 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Thu, 13 Jun 2024 14:45:06 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B07571=20-=20:arrow=5Fup:=20Bump=20HTML2Te?= =?UTF-8?q?xt=20library=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/Html2Text.php | 331 ---- application/Html2TextException.php | 28 - application/utils.inc.php | 3 +- composer.json | 3 +- composer.lock | 57 +- lib/composer/autoload_classmap.php | 14 +- lib/composer/autoload_psr4.php | 1 + lib/composer/autoload_static.php | 19 +- lib/composer/installed.json | 58 + lib/composer/installed.php | 13 +- lib/soundasleep/html2text/.editorconfig | 23 + lib/soundasleep/html2text/.gitignore | 5 + lib/soundasleep/html2text/.travis.yml | 10 + lib/soundasleep/html2text/CHANGELOG.md | 37 + lib/soundasleep/html2text/LICENSE.md | 21 + lib/soundasleep/html2text/README.md | 101 ++ lib/soundasleep/html2text/composer.json | 32 + lib/soundasleep/html2text/composer.lock | 1586 +++++++++++++++++ lib/soundasleep/html2text/convert.php | 21 + lib/soundasleep/html2text/html2text.php | 16 + lib/soundasleep/html2text/phpunit.xml | 8 + lib/soundasleep/html2text/src/Html2Text.php | 505 ++++++ .../html2text/src/Html2TextException.php | 14 + .../unitary-tests/core/ActionEmailTest.php | 4 +- 24 files changed, 2529 insertions(+), 381 deletions(-) delete mode 100644 application/Html2Text.php delete mode 100644 application/Html2TextException.php create mode 100644 lib/soundasleep/html2text/.editorconfig create mode 100644 lib/soundasleep/html2text/.gitignore create mode 100644 lib/soundasleep/html2text/.travis.yml create mode 100644 lib/soundasleep/html2text/CHANGELOG.md create mode 100644 lib/soundasleep/html2text/LICENSE.md create mode 100644 lib/soundasleep/html2text/README.md create mode 100644 lib/soundasleep/html2text/composer.json create mode 100644 lib/soundasleep/html2text/composer.lock create mode 100644 lib/soundasleep/html2text/convert.php create mode 100644 lib/soundasleep/html2text/html2text.php create mode 100644 lib/soundasleep/html2text/phpunit.xml create mode 100644 lib/soundasleep/html2text/src/Html2Text.php create mode 100644 lib/soundasleep/html2text/src/Html2TextException.php diff --git a/application/Html2Text.php b/application/Html2Text.php deleted file mode 100644 index 6b3c898a6..000000000 --- a/application/Html2Text.php +++ /dev/null @@ -1,331 +0,0 @@ - - * @copyright Copyright 2012 Sean Murphy. All rights reserved. - * @license http://creativecommons.org/publicdomain/zero/1.0/ - * @link http://php.net/manual/function.str-replace.php - * - * @param mixed $search - * @param mixed $replace - * @param mixed $subject - * @param int $count - * @return mixed - */ -function mb_str_replace($search, $replace, $subject, &$count = 0) { - if (!is_array($subject)) { - // Normalize $search and $replace so they are both arrays of the same length - $searches = is_array($search) ? array_values($search) : array($search); - $replacements = is_array($replace) ? array_values($replace) : array($replace); - $replacements = array_pad($replacements, count($searches), ''); - foreach ($searches as $key => $search) { - $parts = mb_split(preg_quote($search), $subject); - if (is_array($parts)) - { - $count += count($parts) - 1; - $subject = implode($replacements[$key], $parts); - } - } - } else { - // Call mb_str_replace for each subject in array, recursively - foreach ($subject as $key => $value) { - $subject[$key] = mb_str_replace($search, $replace, $value, $count); - } - } - return $subject; -} - -/****************************************************************************** - * Copyright (c) 2010 Jevon Wright and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * or - * - * LGPL which is available at http://www.gnu.org/licenses/lgpl.html - * - * - * Contributors: - * Jevon Wright - initial API and implementation - * Denis Flaven - some fixes for properly handling UTF-8 characters - ****************************************************************************/ - -class Html2Text { - - /** - * Tries to convert the given HTML into a plain text format - best suited for - * e-mail display, etc. - * - *

In particular, it tries to maintain the following features: - *

- * - * @param string html the input HTML - * @return string the HTML converted, as best as possible, to text - * @throws Html2TextException if the HTML could not be loaded as a {@link DOMDocument} - */ - static function convert($html) { - // replace   with spaces - - $html = str_replace(" ", " ", $html); - $html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8 - - $html = static::fixNewlines($html); - - $doc = new \DOMDocument(); - if (!@$doc->loadHTML(''.$html)) // Forces the UTF-8 character set for HTML fragments - { - throw new Html2TextException("Could not load HTML - badly formed?", $html); - } - - $output = static::iterateOverNode($doc); - - // remove leading and trailing spaces on each line - $output = preg_replace("/[ \t]*\n[ \t]*/im", "\n", $output); - $output = preg_replace("/ *\t */im", "\t", $output); - - // remove unnecessary empty lines - $output = preg_replace("/\n\n\n*/im", "\n\n", $output); - - // remove leading and trailing whitespace - $output = trim($output); - - return $output; - } - - /** - * Unify newlines; in particular, \r\n becomes \n, and - * then \r becomes \n. This means that all newlines (Unix, Windows, Mac) - * all become \ns. - * - * @param string text text with any number of \r, \r\n and \n combinations - * @return string the fixed text - */ - static function fixNewlines($text) { - // replace \r\n to \n - $text = str_replace("\r\n", "\n", $text); - // remove \rs - $text = str_replace("\r", "\n", $text); - - return $text; - } - - static function nextChildName($node) { - // get the next child - $nextNode = $node->nextSibling; - while ($nextNode != null) { - if ($nextNode instanceof \DOMElement) { - break; - } - $nextNode = $nextNode->nextSibling; - } - $nextName = null; - if ($nextNode instanceof \DOMElement && $nextNode != null) { - $nextName = strtolower($nextNode->nodeName); - } - - return $nextName; - } - - static function prevChildName($node) { - // get the previous child - $nextNode = $node->previousSibling; - while ($nextNode != null) { - if ($nextNode instanceof \DOMElement) { - break; - } - $nextNode = $nextNode->previousSibling; - } - $nextName = null; - if ($nextNode instanceof \DOMElement && $nextNode != null) { - $nextName = strtolower($nextNode->nodeName); - } - - return $nextName; - } - - static function iterateOverNode($node) { - if ($node instanceof \DOMText) { - // Replace whitespace characters with a space (equivilant to \s) - return preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $node->wholeText); - } - if ($node instanceof \DOMDocumentType) { - // ignore - return ""; - } - - $nextName = static::nextChildName($node); - $prevName = static::prevChildName($node); - - $name = strtolower($node->nodeName); - - // start whitespace - switch ($name) { - case "hr": - return "---------------------------------------------------------------\n"; - - case "style": - case "head": - case "title": - case "meta": - case "script": - // ignore these tags - return ""; - - case "h1": - case "h2": - case "h3": - case "h4": - case "h5": - case "h6": - case "ol": - case "ul": - // add two newlines, second line is added below - $output = "\n"; - break; - - case "td": - case "th": - // add tab char to separate table fields - $output = "\t"; - break; - - case "tr": - case "p": - case "div": - // add one line - $output = "\n"; - break; - - case "li": - $output = "- "; - break; - - default: - // print out contents of unknown tags - $output = ""; - break; - } - - // debug - //$output .= "[$name,$nextName]"; - - if (isset($node->childNodes)) { - for ($i = 0; $i < $node->childNodes->length; $i++) { - $n = $node->childNodes->item($i); - - $text = static::iterateOverNode($n); - - $output .= $text; - } - } - - // end whitespace - switch ($name) { - case "h1": - case "h2": - case "h3": - case "h4": - case "h5": - case "h6": - $output .= "\n"; - break; - - case "p": - case "br": - // add one line - if ($nextName != "div") - $output .= "\n"; - break; - - case "div": - // add one line only if the next child isn't a div - if ($nextName != "div" && $nextName != null) - $output .= "\n"; - break; - - case "a": - // links are returned in [text](link) format - $href = $node->getAttribute("href"); - - $output = trim($output); - - // remove double [[ ]] s from linking images - if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") { - $output = substr($output, 1, strlen($output) - 2); - - // for linking images, the title of the overrides the title of the - if ($node->getAttribute("title")) { - $output = $node->getAttribute("title"); - } - } - - // if there is no link text, but a title attr - if (!$output && $node->getAttribute("title")) { - $output = $node->getAttribute("title"); - } - - if ($href == null) { - // it doesn't link anywhere - if ($node->getAttribute("name") != null) { - $output = "[$output]"; - } - } else { - if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") { - // link to the same address: just use link - $output; - } else { - // replace it - if ($output) { - $output = "[$output]($href)"; - } else { - // empty string - $output = $href; - } - } - } - - // does the next node require additional whitespace? - switch ($nextName) { - case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": - $output .= "\n"; - break; - } - break; - - case "img": - if ($node->getAttribute("title")) { - $output = "[" . $node->getAttribute("title") . "]"; - } elseif ($node->getAttribute("alt")) { - $output = "[" . $node->getAttribute("alt") . "]"; - } else { - $output = ""; - } - break; - - case "li": - $output .= "\n"; - break; - - default: - // do nothing - } - - return $output; - } - -} diff --git a/application/Html2TextException.php b/application/Html2TextException.php deleted file mode 100644 index ddfa86586..000000000 --- a/application/Html2TextException.php +++ /dev/null @@ -1,28 +0,0 @@ -more_info = $more_info; - } -} diff --git a/application/utils.inc.php b/application/utils.inc.php index 48e53f4bd..887df1ab2 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -26,6 +26,7 @@ use Combodo\iTop\Service\Module\ModuleService; use ScssPhp\ScssPhp\Compiler; use ScssPhp\ScssPhp\OutputStyle; use ScssPhp\ScssPhp\ValueConverter; +use Soundasleep\Html2Text; /** @@ -2067,7 +2068,7 @@ SQL; { try { //return ''.$sHtml; - return \Html2Text\Html2Text::convert(''.$sHtml); + return Html2Text::convert(''.$sHtml); } catch (Exception $e) { return $e->getMessage(); diff --git a/composer.json b/composer.json index 8adc82473..82774ff05 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "symfony/yaml": "~6.4.0", "tecnickcom/tcpdf": "^6.6.0", "thenetworg/oauth2-azure": "^2.0", - "masterminds/html5": "^2.8.0" + "masterminds/html5": "^2.8.0", + "soundasleep/html2text": "~1.1" }, "require-dev": { "symfony/debug-bundle": "~6.4.0", diff --git a/composer.lock b/composer.lock index b093c2cd5..2541265fa 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c8ab1de6659dd4fa2ded6043615b86e6", + "content-hash": "da75a60a57f3c216e40d472f4d98d026", "packages": [ { "name": "apereo/phpcas", @@ -2041,6 +2041,61 @@ }, "time": "2024-01-13T12:36:40+00:00" }, + { + "name": "soundasleep/html2text", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/soundasleep/html2text.git", + "reference": "3243a7107878a61685d2eccf99918d6479e039fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/soundasleep/html2text/zipball/3243a7107878a61685d2eccf99918d6479e039fc", + "reference": "3243a7107878a61685d2eccf99918d6479e039fc", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "~7.0", + "soundasleep/component-tests": "~0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Soundasleep\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jevon Wright", + "homepage": "https://jevon.org", + "role": "Developer" + } + ], + "description": "A PHP script to convert HTML into a plain text format", + "homepage": "https://github.com/soundasleep/html2text", + "keywords": [ + "email", + "html", + "php", + "text" + ], + "support": { + "email": "support@jevon.org", + "issues": "https://github.com/soundasleep/html2text/issues", + "source": "https://github.com/soundasleep/html2text/tree/master" + }, + "time": "2019-02-15T01:44:54+00:00" + }, { "name": "symfony/cache", "version": "v6.4.2", diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index f16c24d02..d08b22f6f 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -746,8 +746,6 @@ return array( 'HTMLDOMSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', 'HTMLNullSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', 'HTMLSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', - 'Html2Text\\Html2Text' => $baseDir . '/application/Html2Text.php', - 'Html2Text\\Html2TextException' => $baseDir . '/application/Html2TextException.php', 'ITopArchiveTar' => $baseDir . '/core/tar-itop.class.inc.php', 'InlineImage' => $baseDir . '/core/inlineimage.class.inc.php', 'InlineImageGC' => $baseDir . '/core/inlineimage.class.inc.php', @@ -1152,11 +1150,11 @@ return array( 'ModuleHandlerApiInterface' => $baseDir . '/core/modulehandler.class.inc.php', 'MonthlyRotatingLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'MyHelpers' => $baseDir . '/core/MyHelpers.class.inc.php', - 'MySQLException' => $baseDir . '/application/exceptions/mysql/MySQLException.php', - 'MySQLHasGoneAwayException' => $baseDir . '/application/exceptions/mysql/MySQLHasGoneAwayException.php', - 'MySQLNoTransactionException' => $baseDir . '/application/exceptions/mysql/MySQLNoTransactionException.php', - 'MySQLQueryHasNoResultException' => $baseDir . '/application/exceptions/mysql/MySQLQueryHasNoResultException.php', - 'MySQLTransactionNotClosedException' => $baseDir . '/application/exceptions/mysql/MySQLTransactionNotClosedException.php', + 'MySQLException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLHasGoneAwayException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLNoTransactionException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLQueryHasNoResultException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLTransactionNotClosedException' => $baseDir . '/core/cmdbsource.class.inc.php', 'NestedQueryExpression' => $baseDir . '/core/oql/expression.class.inc.php', 'NestedQueryOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', 'NewObjectMenuNode' => $baseDir . '/application/menunode.class.inc.php', @@ -1646,6 +1644,8 @@ return array( 'SimpleCryptSodiumEngine' => $baseDir . '/core/simplecrypt.class.inc.php', 'SimpleGraph' => $baseDir . '/core/simplegraph.class.inc.php', 'SimpleGraphException' => $baseDir . '/core/simplegraph.class.inc.php', + 'Soundasleep\\Html2Text' => $vendorDir . '/soundasleep/html2text/src/Html2Text.php', + 'Soundasleep\\Html2TextException' => $vendorDir . '/soundasleep/html2text/src/Html2TextException.php', 'SpreadsheetBulkExport' => $baseDir . '/core/spreadsheetbulkexport.class.inc.php', 'StimulusChecker' => $baseDir . '/core/userrights.class.inc.php', 'StimulusInternal' => $baseDir . '/core/stimulus.class.inc.php', diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 632d6d2df..594907f1a 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -46,6 +46,7 @@ return array( 'Symfony\\Bundle\\FrameworkBundle\\' => array($vendorDir . '/symfony/framework-bundle'), 'Symfony\\Bundle\\DebugBundle\\' => array($vendorDir . '/symfony/debug-bundle'), 'Symfony\\Bridge\\Twig\\' => array($vendorDir . '/symfony/twig-bridge'), + 'Soundasleep\\' => array($vendorDir . '/soundasleep/html2text/src'), 'ScssPhp\\ScssPhp\\' => array($vendorDir . '/scssphp/scssphp/src'), 'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 8f1e52d1c..b1d9534ea 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -74,6 +74,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Symfony\\Bundle\\FrameworkBundle\\' => 31, 'Symfony\\Bundle\\DebugBundle\\' => 27, 'Symfony\\Bridge\\Twig\\' => 20, + 'Soundasleep\\' => 12, 'ScssPhp\\ScssPhp\\' => 16, 'Sabberworm\\CSS\\' => 15, ), @@ -275,6 +276,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f array ( 0 => __DIR__ . '/..' . '/symfony/twig-bridge', ), + 'Soundasleep\\' => + array ( + 0 => __DIR__ . '/..' . '/soundasleep/html2text/src', + ), 'ScssPhp\\ScssPhp\\' => array ( 0 => __DIR__ . '/..' . '/scssphp/scssphp/src', @@ -1129,8 +1134,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'HTMLDOMSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', 'HTMLNullSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', 'HTMLSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', - 'Html2Text\\Html2Text' => __DIR__ . '/../..' . '/application/Html2Text.php', - 'Html2Text\\Html2TextException' => __DIR__ . '/../..' . '/application/Html2TextException.php', 'ITopArchiveTar' => __DIR__ . '/../..' . '/core/tar-itop.class.inc.php', 'InlineImage' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', 'InlineImageGC' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', @@ -1535,11 +1538,11 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'ModuleHandlerApiInterface' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php', 'MonthlyRotatingLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'MyHelpers' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php', - 'MySQLException' => __DIR__ . '/../..' . '/application/exceptions/mysql/MySQLException.php', - 'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/application/exceptions/mysql/MySQLHasGoneAwayException.php', - 'MySQLNoTransactionException' => __DIR__ . '/../..' . '/application/exceptions/mysql/MySQLNoTransactionException.php', - 'MySQLQueryHasNoResultException' => __DIR__ . '/../..' . '/application/exceptions/mysql/MySQLQueryHasNoResultException.php', - 'MySQLTransactionNotClosedException' => __DIR__ . '/../..' . '/application/exceptions/mysql/MySQLTransactionNotClosedException.php', + 'MySQLException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLNoTransactionException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLQueryHasNoResultException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLTransactionNotClosedException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', 'NestedQueryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', 'NestedQueryOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', 'NewObjectMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', @@ -2029,6 +2032,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'SimpleCryptSodiumEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', 'SimpleGraph' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', 'SimpleGraphException' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'Soundasleep\\Html2Text' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2Text.php', + 'Soundasleep\\Html2TextException' => __DIR__ . '/..' . '/soundasleep/html2text/src/Html2TextException.php', 'SpreadsheetBulkExport' => __DIR__ . '/../..' . '/core/spreadsheetbulkexport.class.inc.php', 'StimulusChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', 'StimulusInternal' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', diff --git a/lib/composer/installed.json b/lib/composer/installed.json index 44d865ac7..9f278848e 100644 --- a/lib/composer/installed.json +++ b/lib/composer/installed.json @@ -2128,6 +2128,64 @@ }, "install-path": "../scssphp/scssphp" }, + { + "name": "soundasleep/html2text", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/soundasleep/html2text.git", + "reference": "3243a7107878a61685d2eccf99918d6479e039fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/soundasleep/html2text/zipball/3243a7107878a61685d2eccf99918d6479e039fc", + "reference": "3243a7107878a61685d2eccf99918d6479e039fc", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "~7.0", + "soundasleep/component-tests": "~0.2" + }, + "time": "2019-02-15T01:44:54+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Soundasleep\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jevon Wright", + "homepage": "https://jevon.org", + "role": "Developer" + } + ], + "description": "A PHP script to convert HTML into a plain text format", + "homepage": "https://github.com/soundasleep/html2text", + "keywords": [ + "email", + "html", + "php", + "text" + ], + "support": { + "email": "support@jevon.org", + "issues": "https://github.com/soundasleep/html2text/issues", + "source": "https://github.com/soundasleep/html2text/tree/master" + }, + "install-path": "../soundasleep/html2text" + }, { "name": "symfony/cache", "version": "v6.4.2", diff --git a/lib/composer/installed.php b/lib/composer/installed.php index 16887e28c..3b63bb7b3 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'combodo/itop', 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => '0036c70fbd34e0bfbdfdbaf26d497d8e1ed7bd04', + 'reference' => 'bfbb046b10aa2fc864d5fbc794df31e488542df4', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -22,7 +22,7 @@ 'combodo/itop' => array( 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => '0036c70fbd34e0bfbdfdbaf26d497d8e1ed7bd04', + 'reference' => 'bfbb046b10aa2fc864d5fbc794df31e488542df4', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -359,6 +359,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'soundasleep/html2text' => array( + 'pretty_version' => '1.1.0', + 'version' => '1.1.0.0', + 'reference' => '3243a7107878a61685d2eccf99918d6479e039fc', + 'type' => 'library', + 'install_path' => __DIR__ . '/../soundasleep/html2text', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'symfony/cache' => array( 'pretty_version' => 'v6.4.2', 'version' => '6.4.2.0', diff --git a/lib/soundasleep/html2text/.editorconfig b/lib/soundasleep/html2text/.editorconfig new file mode 100644 index 000000000..7974b6a8a --- /dev/null +++ b/lib/soundasleep/html2text/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = 4 + +[*.md] +indent_style = space +indent_size = 2 + +# don't add newlines to test files +[tests/*] +indent_style = tabs +trim_trailing_whitespace = false +insert_final_newline = false diff --git a/lib/soundasleep/html2text/.gitignore b/lib/soundasleep/html2text/.gitignore new file mode 100644 index 000000000..255c0b0a9 --- /dev/null +++ b/lib/soundasleep/html2text/.gitignore @@ -0,0 +1,5 @@ +tests/*.output +*.sublime-project +*.sublime-workspace +vendor/ +**/*.DS_Store diff --git a/lib/soundasleep/html2text/.travis.yml b/lib/soundasleep/html2text/.travis.yml new file mode 100644 index 000000000..3f03a9eee --- /dev/null +++ b/lib/soundasleep/html2text/.travis.yml @@ -0,0 +1,10 @@ +language: php +php: + - 7.3 +group: stable +before_install: + - composer self-update +install: + - composer install +script: + - ./vendor/bin/phpunit diff --git a/lib/soundasleep/html2text/CHANGELOG.md b/lib/soundasleep/html2text/CHANGELOG.md new file mode 100644 index 000000000..a1bda0954 --- /dev/null +++ b/lib/soundasleep/html2text/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.1.0] - 2019-02-15 +### Added +- Zero-width non-joiners are now stripped to prevent output issues, similar to non-breaking whitespace + +### Fixed +- Fix namespace in composer [#67](https://github.com/soundasleep/html2text/pull/67) + +## [1.0.0] - 2019-02-14 +### Added +- Added `drop_links` option to render links without the target href [#65](https://github.com/soundasleep/html2text/pull/65) + +### Changed +- **Important:** Changed namespace from `\Html2Text\Html2Text` to `\Soundasleep\Html2text` [#45](https://github.com/soundasleep/html2text/issues/45) +- Treat non-breaking spaces consistently: never include them in output text [#64](https://github.com/soundasleep/html2text/pull/64) +- Second argument to `convert()` is now an array, rather than boolean [#65](https://github.com/soundasleep/html2text/pull/65) +- Optimise/improve newline & whitespace handling [#47](https://github.com/soundasleep/html2text/pull/47) +- Upgrade PHP support to PHP 7.3+ +- Upgrade PHPUnit to 7.x +- Re-release project under MIT license [#58](https://github.com/soundasleep/html2text/issues/58) + +## [0.5.0] - 2017-04-20 +### Added +- Add ignore_error optional argument [#63](https://github.com/soundasleep/html2text/pull/63) +- Blockquote support [#50](https://github.com/soundasleep/html2text/pull/50) + +[Unreleased]: https://github.com/soundasleep/html2text/compare/1.1.0...HEAD +[1.1.0]: https://github.com/soundasleep/html2text/compare/1.0.0...1.1.0 +[1.0.0]: https://github.com/soundasleep/html2text/compare/0.5.0...1.0.0 +[0.5.0]: https://github.com/soundasleep/html2text/compare/0.5.0...0.3.4 diff --git a/lib/soundasleep/html2text/LICENSE.md b/lib/soundasleep/html2text/LICENSE.md new file mode 100644 index 000000000..d8613be25 --- /dev/null +++ b/lib/soundasleep/html2text/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Jevon Wright + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/soundasleep/html2text/README.md b/lib/soundasleep/html2text/README.md new file mode 100644 index 000000000..c28034c12 --- /dev/null +++ b/lib/soundasleep/html2text/README.md @@ -0,0 +1,101 @@ +html2text [![Build Status](https://travis-ci.org/soundasleep/html2text.svg?branch=master)](https://travis-ci.org/soundasleep/html2text) [![Total Downloads](https://poser.pugx.org/soundasleep/html2text/downloads.png)](https://packagist.org/packages/soundasleep/html2text) +========= + +html2text is a very simple script that uses DOM methods to convert HTML into a format similar to what would be +rendered by a browser - perfect for places where you need a quick text representation. For example: + +```html + +Ignored Title + +

Hello, World!

+ +

This is some e-mail content. + Even though it has whitespace and newlines, the e-mail converter + will handle it correctly. + +

Even mismatched tags.

+ +
A div
+
Another div
+
A div
within a div
+ +
A link + + + +``` + +Will be converted into: + +```text +Hello, World! + +This is some e-mail content. Even though it has whitespace and newlines, the e-mail converter will handle it correctly. + +Even mismatched tags. + +A div +Another div +A div +within a div + +[A link](http://foo.com) +``` + +See the [original blog post](http://journals.jevon.org/users/jevon-phd/entry/19818) or the related [StackOverflow answer](http://stackoverflow.com/a/2564472/39531). + +## Installing + +You can use [Composer](http://getcomposer.org/) to add the [package](https://packagist.org/packages/soundasleep/html2text) to your project: + +```json +{ + "require": { + "soundasleep/html2text": "~1.1" + } +} +``` + +And then use it quite simply: + +```php +$text = \Soundasleep\Html2Text::convert($html); +``` + +You can also include the supplied `html2text.php` and use `$text = convert_html_to_text($html);` instead. + +### Options + +| Option | Default | Description | +|--------|---------|-------------| +| **ignore_errors** | `false` | Set to `true` to ignore any XML parsing errors. | +| **drop_links** | `false` | Set to `true` to not render links as `[http://foo.com](My Link)`, but rather just `My Link`. | + +Pass along options as a second argument to `convert`, for example: + +```php +$options = array( + 'ignore_errors' => true, + // other options go here +); +$text = \Soundasleep\Html2Text::convert($html, $options); +``` + +## Tests + +Some very basic tests are provided in the `tests/` directory. Run them with `composer install && vendor/bin/phpunit`. + +## Troubleshooting + +### Class 'DOMDocument' not found + +You need to [install the PHP XML extension](https://github.com/soundasleep/html2text/issues/55) for your PHP version. e.g. `apt-get install php7.1-xml` + +## License + +`html2text` is [licensed under MIT](LICENSE.md), making it suitable for both Eclipse and GPL projects. + +## Other versions + +Also see [html2text_ruby](https://github.com/soundasleep/html2text_ruby), a Ruby implementation. diff --git a/lib/soundasleep/html2text/composer.json b/lib/soundasleep/html2text/composer.json new file mode 100644 index 000000000..409bb9d0d --- /dev/null +++ b/lib/soundasleep/html2text/composer.json @@ -0,0 +1,32 @@ +{ + "name": "soundasleep/html2text", + "description": "A PHP script to convert HTML into a plain text format", + "type": "library", + "keywords": [ "php", "html", "text", "email" ], + "homepage": "https://github.com/soundasleep/html2text", + "license": "MIT", + "authors": [ + { + "name": "Jevon Wright", + "homepage": "https://jevon.org", + "role": "Developer" + } + ], + "autoload": { + "psr-4": { + "Soundasleep\\": "src" + } + }, + "support": { + "email": "support@jevon.org" + }, + "require": { + "php": ">=7.0", + "ext-dom": "*", + "ext-libxml": "*" + }, + "require-dev": { + "phpunit/phpunit": "~7.0", + "soundasleep/component-tests": "~0.2" + } +} diff --git a/lib/soundasleep/html2text/composer.lock b/lib/soundasleep/html2text/composer.lock new file mode 100644 index 000000000..0e084dc91 --- /dev/null +++ b/lib/soundasleep/html2text/composer.lock @@ -0,0 +1,1586 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4eb1764bda1a63f84b468db3da77e779", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2017-07-22T11:58:36+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.8", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4", + "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2019-01-14T23:55:14+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2018-06-11T23:09:50+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-30T07:14:17+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-08-05T17:53:17+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "6.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "ext-xdebug": "^2.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-10-31T16:06:48+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", + "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2018-02-01T13:07:23+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2018-10-30T05:52:18+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "7.5.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "2896657da5fb237bc316bdfc18c2650efeee0dc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2896657da5fb237bc316bdfc18c2650efeee0dc0", + "reference": "2896657da5fb237bc316bdfc18c2650efeee0dc0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.0", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2019-02-07T14:15:04+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-02-04T06:01:07+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656", + "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2019-02-01T05:27:49+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "soundasleep/component-tests", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/soundasleep/component-tests.git", + "reference": "7525ffd30ad2406f745ff94729717fe7bbfa2466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/soundasleep/component-tests/zipball/7525ffd30ad2406f745ff94729717fe7bbfa2466", + "reference": "7525ffd30ad2406f745ff94729717fe7bbfa2466", + "shasum": "" + }, + "require": { + "justinrainbow/json-schema": "~5.2", + "phpunit/phpunit": "~7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ComponentTests\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "description": "Common Composer and PHP component lint and validation tests", + "time": "2019-02-13T21:21:24+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-08-06T14:22:27+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-12-25T11:19:39+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.0", + "ext-dom": "*", + "ext-libxml": "*" + }, + "platform-dev": [] +} diff --git a/lib/soundasleep/html2text/convert.php b/lib/soundasleep/html2text/convert.php new file mode 100644 index 000000000..48094eb21 --- /dev/null +++ b/lib/soundasleep/html2text/convert.php @@ -0,0 +1,21 @@ + + + + + tests + + + diff --git a/lib/soundasleep/html2text/src/Html2Text.php b/lib/soundasleep/html2text/src/Html2Text.php new file mode 100644 index 000000000..5153cefcf --- /dev/null +++ b/lib/soundasleep/html2text/src/Html2Text.php @@ -0,0 +1,505 @@ + false, + 'drop_links' => false, + ); + } + + /** + * Tries to convert the given HTML into a plain text format - best suited for + * e-mail display, etc. + * + *

In particular, it tries to maintain the following features: + *

+ * + * @param string $html the input HTML + * @param boolean $ignore_error Ignore xml parsing errors + * @return string the HTML converted, as best as possible, to text + * @throws Html2TextException if the HTML could not be loaded as a {@link \DOMDocument} + */ + public static function convert($html, $options = array()) { + + if ($options === false || $options === true) { + // Using old style (< 1.0) of passing in options + $options = array('ignore_errors' => $options); + } + + $options = array_merge(static::defaultOptions(), $options); + + // check all options are valid + foreach ($options as $key => $value) { + if (!in_array($key, array_keys(static::defaultOptions()))) { + throw new \InvalidArgumentException("Unknown html2text option '$key'"); + } + } + + $is_office_document = static::isOfficeDocument($html); + + if ($is_office_document) { + // remove office namespace + $html = str_replace(array("", ""), "", $html); + } + + $html = static::fixNewlines($html); + if (mb_detect_encoding($html, "UTF-8", true)) { + $html = mb_convert_encoding($html, "HTML-ENTITIES", "UTF-8"); + } + + $doc = static::getDocument($html, $options['ignore_errors']); + + $output = static::iterateOverNode($doc, null, false, $is_office_document, $options); + + // process output for whitespace/newlines + $output = static::processWhitespaceNewlines($output); + + return $output; + } + + /** + * Unify newlines; in particular, \r\n becomes \n, and + * then \r becomes \n. This means that all newlines (Unix, Windows, Mac) + * all become \ns. + * + * @param string $text text with any number of \r, \r\n and \n combinations + * @return string the fixed text + */ + static function fixNewlines($text) { + // replace \r\n to \n + $text = str_replace("\r\n", "\n", $text); + // remove \rs + $text = str_replace("\r", "\n", $text); + + return $text; + } + + static function nbspCodes() { + return array( + "\xc2\xa0", + "\u00a0", + ); + } + + static function zwnjCodes() { + return array( + "\xe2\x80\x8c", + "\u200c", + ); + } + + /** + * Remove leading or trailing spaces and excess empty lines from provided multiline text + * + * @param string $text multiline text any number of leading or trailing spaces or excess lines + * @return string the fixed text + */ + static function processWhitespaceNewlines($text) { + + // remove excess spaces around tabs + $text = preg_replace("/ *\t */im", "\t", $text); + + // remove leading whitespace + $text = ltrim($text); + + // remove leading spaces on each line + $text = preg_replace("/\n[ \t]*/im", "\n", $text); + + // convert non-breaking spaces to regular spaces to prevent output issues, + // do it here so they do NOT get removed with other leading spaces, as they + // are sometimes used for indentation + $text = static::renderText($text); + + // remove trailing whitespace + $text = rtrim($text); + + // remove trailing spaces on each line + $text = preg_replace("/[ \t]*\n/im", "\n", $text); + + // unarmor pre blocks + $text = static::fixNewLines($text); + + // remove unnecessary empty lines + $text = preg_replace("/\n\n\n*/im", "\n\n", $text); + + return $text; + } + + /** + * Parse HTML into a DOMDocument + * + * @param string $html the input HTML + * @param boolean $ignore_error Ignore xml parsing errors + * @return \DOMDocument the parsed document tree + */ + static function getDocument($html, $ignore_error = false) { + + $doc = new \DOMDocument(); + + $html = trim($html); + + if (!$html) { + // DOMDocument doesn't support empty value and throws an error + // Return empty document instead + return $doc; + } + + if ($html[0] !== '<') { + // If HTML does not begin with a tag, we put a body tag around it. + // If we do not do this, PHP will insert a paragraph tag around + // the first block of text for some reason which can mess up + // the newlines. See pre.html test for an example. + $html = '' . $html . ''; + } + + if ($ignore_error) { + $doc->strictErrorChecking = false; + $doc->recover = true; + $doc->xmlStandalone = true; + $old_internal_errors = libxml_use_internal_errors(true); + $load_result = $doc->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NONET | LIBXML_PARSEHUGE); + libxml_use_internal_errors($old_internal_errors); + } + else { + $load_result = $doc->loadHTML($html); + } + + if (!$load_result) { + throw new Html2TextException("Could not load HTML - badly formed?", $html); + } + + return $doc; + } + + /** + * Can we guess that this HTML is generated by Microsoft Office? + */ + static function isOfficeDocument($html) { + return strpos($html, "urn:schemas-microsoft-com:office") !== false; + } + + /** + * Replace any special characters with simple text versions, to prevent output issues: + * - Convert non-breaking spaces to regular spaces; and + * - Convert zero-width non-joiners to '' (nothing). + * + * This is to match our goal of rendering documents as they would be rendered + * by a browser. + */ + static function renderText($text) { + $text = str_replace(static::nbspCodes(), " ", $text); + $text = str_replace(static::zwnjCodes(), "", $text); + return $text; + } + + static function isWhitespace($text) { + return strlen(trim(static::renderText($text), "\n\r\t ")) === 0; + } + + static function nextChildName($node) { + // get the next child + $nextNode = $node->nextSibling; + while ($nextNode != null) { + if ($nextNode instanceof \DOMText) { + if (!static::isWhitespace($nextNode->wholeText)) { + break; + } + } + + if ($nextNode instanceof \DOMElement) { + break; + } + + $nextNode = $nextNode->nextSibling; + } + + $nextName = null; + if (($nextNode instanceof \DOMElement || $nextNode instanceof \DOMText) && $nextNode != null) { + $nextName = strtolower($nextNode->nodeName); + } + + return $nextName; + } + + static function iterateOverNode($node, $prevName = null, $in_pre = false, $is_office_document = false, $options) { + if ($node instanceof \DOMText) { + // Replace whitespace characters with a space (equivilant to \s) + if ($in_pre) { + $text = "\n" . trim(static::renderText($node->wholeText), "\n\r\t ") . "\n"; + + // Remove trailing whitespace only + $text = preg_replace("/[ \t]*\n/im", "\n", $text); + + // armor newlines with \r. + return str_replace("\n", "\r", $text); + + } else { + $text = static::renderText($node->wholeText); + $text = preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $text); + + if (!static::isWhitespace($text) && ($prevName == 'p' || $prevName == 'div')) { + return "\n" . $text; + } + return $text; + } + } + + if ($node instanceof \DOMDocumentType || $node instanceof \DOMProcessingInstruction) { + // ignore + return ""; + } + + $name = strtolower($node->nodeName); + $nextName = static::nextChildName($node); + + // start whitespace + switch ($name) { + case "hr": + $prefix = ''; + if ($prevName != null) { + $prefix = "\n"; + } + return $prefix . "---------------------------------------------------------------\n"; + + case "style": + case "head": + case "title": + case "meta": + case "script": + // ignore these tags + return ""; + + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + case "ol": + case "ul": + case "pre": + // add two newlines + $output = "\n\n"; + break; + + case "td": + case "th": + // add tab char to separate table fields + $output = "\t"; + break; + + case "p": + // Microsoft exchange emails often include HTML which, when passed through + // html2text, results in lots of double line returns everywhere. + // + // To fix this, for any p element with a className of `MsoNormal` (the standard + // classname in any Microsoft export or outlook for a paragraph that behaves + // like a line return) we skip the first line returns and set the name to br. + if ($is_office_document && $node->getAttribute('class') == 'MsoNormal') { + $output = ""; + $name = 'br'; + break; + } + + // add two lines + $output = "\n\n"; + break; + + case "tr": + // add one line + $output = "\n"; + break; + + case "div": + $output = ""; + if ($prevName !== null) { + // add one line + $output .= "\n"; + } + break; + + case "li": + $output = "- "; + break; + + default: + // print out contents of unknown tags + $output = ""; + break; + } + + // debug + //$output .= "[$name,$nextName]"; + + if (isset($node->childNodes)) { + + $n = $node->childNodes->item(0); + $previousSiblingNames = array(); + $previousSiblingName = null; + + $parts = array(); + $trailing_whitespace = 0; + + while ($n != null) { + + $text = static::iterateOverNode($n, $previousSiblingName, $in_pre || $name == 'pre', $is_office_document, $options); + + // Pass current node name to next child, as previousSibling does not appear to get populated + if ($n instanceof \DOMDocumentType + || $n instanceof \DOMProcessingInstruction + || ($n instanceof \DOMText && static::isWhitespace($text))) { + // Keep current previousSiblingName, these are invisible + $trailing_whitespace++; + } + else { + $previousSiblingName = strtolower($n->nodeName); + $previousSiblingNames[] = $previousSiblingName; + $trailing_whitespace = 0; + } + + $node->removeChild($n); + $n = $node->childNodes->item(0); + + $parts[] = $text; + } + + // Remove trailing whitespace, important for the br check below + while ($trailing_whitespace-- > 0) { + array_pop($parts); + } + + // suppress last br tag inside a node list if follows text + $last_name = array_pop($previousSiblingNames); + if ($last_name === 'br') { + $last_name = array_pop($previousSiblingNames); + if ($last_name === '#text') { + array_pop($parts); + } + } + + $output .= implode('', $parts); + } + + // end whitespace + switch ($name) { + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + case "pre": + case "p": + // add two lines + $output .= "\n\n"; + break; + + case "br": + // add one line + $output .= "\n"; + break; + + case "div": + break; + + case "a": + // links are returned in [text](link) format + $href = $node->getAttribute("href"); + + $output = trim($output); + + // remove double [[ ]] s from linking images + if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") { + $output = substr($output, 1, strlen($output) - 2); + + // for linking images, the title of the overrides the title of the + if ($node->getAttribute("title")) { + $output = $node->getAttribute("title"); + } + } + + // if there is no link text, but a title attr + if (!$output && $node->getAttribute("title")) { + $output = $node->getAttribute("title"); + } + + if ($href == null) { + // it doesn't link anywhere + if ($node->getAttribute("name") != null) { + if ($options['drop_links']) { + $output = "$output"; + } else { + $output = "[$output]"; + } + } + } else { + if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") { + // link to the same address: just use link + $output = "$output"; + } else { + // replace it + if ($output) { + if ($options['drop_links']) { + $output = "$output"; + } else { + $output = "[$output]($href)"; + } + } else { + // empty string + $output = "$href"; + } + } + } + + // does the next node require additional whitespace? + switch ($nextName) { + case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": + $output .= "\n"; + break; + } + break; + + case "img": + if ($node->getAttribute("title")) { + $output = "[" . $node->getAttribute("title") . "]"; + } elseif ($node->getAttribute("alt")) { + $output = "[" . $node->getAttribute("alt") . "]"; + } else { + $output = ""; + } + break; + + case "li": + $output .= "\n"; + break; + + case "blockquote": + // process quoted text for whitespace/newlines + $output = static::processWhitespaceNewlines($output); + + // add leading newline + $output = "\n" . $output; + + // prepend '> ' at the beginning of all lines + $output = preg_replace("/\n/im", "\n> ", $output); + + // replace leading '> >' with '>>' + $output = preg_replace("/\n> >/im", "\n>>", $output); + + // add another leading newline and trailing newlines + $output = "\n" . $output . "\n\n"; + break; + default: + // do nothing + } + + return $output; + } +} diff --git a/lib/soundasleep/html2text/src/Html2TextException.php b/lib/soundasleep/html2text/src/Html2TextException.php new file mode 100644 index 000000000..9171d86ea --- /dev/null +++ b/lib/soundasleep/html2text/src/Html2TextException.php @@ -0,0 +1,14 @@ +more_info = $more_info; + } + +} diff --git a/tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php b/tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php index 41901e46e..afca36584 100644 --- a/tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php +++ b/tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php @@ -4,10 +4,10 @@ namespace Combodo\iTop\Test\UnitTest\Core; use ActionEmail; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use Dict; use Exception; use MetaModel; use utils; -use Dict; /** * @covers \ActionEmail @@ -35,8 +35,6 @@ class ActionEmailTest extends ItopDataTestCase { parent::setUp(); - $this->RequireOnceItopFile('application/Html2Text.php'); - static::$oActionEmail = MetaModel::NewObject('ActionEmail', [ 'name' => 'Test action', 'status' => 'disabled',