From ac940f06fb8a7fefd31c11f0306f10bdbabc4bf6 Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Fri, 5 Oct 2018 15:08:26 -0500 Subject: [PATCH 01/13] added html-to-markdown package https://github.com/thephpleague/html-to-markdown --- .gitignore | 2 + inc/html-to-markdown/CHANGELOG.md | 267 ++++++++++++++++++ inc/html-to-markdown/CONDUCT.md | 22 ++ inc/html-to-markdown/README.md | 196 +++++++++++++ inc/html-to-markdown/composer.json | 48 ++++ inc/html-to-markdown/src/Configuration.php | 76 +++++ .../src/ConfigurationAwareInterface.php | 11 + .../src/Converter/BlockquoteConverter.php | 44 +++ .../src/Converter/CodeConverter.php | 79 ++++++ .../src/Converter/CommentConverter.php | 26 ++ .../src/Converter/ConverterInterface.php | 20 ++ .../src/Converter/DefaultConverter.php | 50 ++++ .../src/Converter/DivConverter.php | 45 +++ .../src/Converter/EmphasisConverter.php | 57 ++++ .../src/Converter/HardBreakConverter.php | 53 ++++ .../src/Converter/HeaderConverter.php | 82 ++++++ .../src/Converter/HorizontalRuleConverter.php | 26 ++ .../src/Converter/ImageConverter.php | 35 +++ .../src/Converter/LinkConverter.php | 65 +++++ .../src/Converter/ListBlockConverter.php | 26 ++ .../src/Converter/ListItemConverter.php | 81 ++++++ .../src/Converter/ParagraphConverter.php | 125 ++++++++ .../src/Converter/PreformattedConverter.php | 58 ++++ .../src/Converter/TextConverter.php | 48 ++++ inc/html-to-markdown/src/Element.php | 257 +++++++++++++++++ inc/html-to-markdown/src/ElementInterface.php | 80 ++++++ inc/html-to-markdown/src/Environment.php | 104 +++++++ inc/html-to-markdown/src/HtmlConverter.php | 251 ++++++++++++++++ .../src/HtmlConverterInterface.php | 26 ++ 29 files changed, 2260 insertions(+) create mode 100755 inc/html-to-markdown/CHANGELOG.md create mode 100755 inc/html-to-markdown/CONDUCT.md create mode 100755 inc/html-to-markdown/README.md create mode 100755 inc/html-to-markdown/composer.json create mode 100755 inc/html-to-markdown/src/Configuration.php create mode 100755 inc/html-to-markdown/src/ConfigurationAwareInterface.php create mode 100755 inc/html-to-markdown/src/Converter/BlockquoteConverter.php create mode 100755 inc/html-to-markdown/src/Converter/CodeConverter.php create mode 100755 inc/html-to-markdown/src/Converter/CommentConverter.php create mode 100755 inc/html-to-markdown/src/Converter/ConverterInterface.php create mode 100755 inc/html-to-markdown/src/Converter/DefaultConverter.php create mode 100755 inc/html-to-markdown/src/Converter/DivConverter.php create mode 100755 inc/html-to-markdown/src/Converter/EmphasisConverter.php create mode 100755 inc/html-to-markdown/src/Converter/HardBreakConverter.php create mode 100755 inc/html-to-markdown/src/Converter/HeaderConverter.php create mode 100755 inc/html-to-markdown/src/Converter/HorizontalRuleConverter.php create mode 100755 inc/html-to-markdown/src/Converter/ImageConverter.php create mode 100755 inc/html-to-markdown/src/Converter/LinkConverter.php create mode 100755 inc/html-to-markdown/src/Converter/ListBlockConverter.php create mode 100755 inc/html-to-markdown/src/Converter/ListItemConverter.php create mode 100755 inc/html-to-markdown/src/Converter/ParagraphConverter.php create mode 100755 inc/html-to-markdown/src/Converter/PreformattedConverter.php create mode 100755 inc/html-to-markdown/src/Converter/TextConverter.php create mode 100755 inc/html-to-markdown/src/Element.php create mode 100755 inc/html-to-markdown/src/ElementInterface.php create mode 100755 inc/html-to-markdown/src/Environment.php create mode 100755 inc/html-to-markdown/src/HtmlConverter.php create mode 100755 inc/html-to-markdown/src/HtmlConverterInterface.php diff --git a/.gitignore b/.gitignore index ed74ecd..0fb8616 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ * !/**/ !*.* + +.DS_Store diff --git a/inc/html-to-markdown/CHANGELOG.md b/inc/html-to-markdown/CHANGELOG.md new file mode 100755 index 0000000..ab07c94 --- /dev/null +++ b/inc/html-to-markdown/CHANGELOG.md @@ -0,0 +1,267 @@ +# Change Log +All notable changes to this project will be documented in this file. +Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. + +## [Unreleased][unreleased] + +## [4.8.0] - 2018-09-18 +### Added + - Added support for email auto-linking + - Added a new interface (`HtmlConverterInterface`) for the main `HtmlConverter` class + - Added additional test cases (#14) + +### Changed + - The `italic_style` option now defaults to `'*'` so that in-word emphasis is handled properly (#75) + +### Fixed + - Fixed several issues of `` and `
` tags not converting to blocks or inlines properly (#26, #70, #102, #140, #161, #162)
+ - Fixed in-word emphasis using underscores as delimiter (#75)
+ - Fixed character escaping inside of `
` elements + - Fixed header edge cases + +### Deprecated + - The `bold_style` and `italic_style` options have been deprecated (#75) + +## [4.7.0] - 2018-05-19 +### Added + - Added `setOptions()` function for chainable calling (#149) + - Added new `list_item_style_alternate` option for converting every-other list with a different character (#155) + +### Fixed + - Fixed insufficient newlines after code blocks (#144, #148) + - Fixed trailing spaces not being preserved in link anchors (#157) + - Fixed list-like lines not being escaped inside of lists items (#159) + +## [4.6.2] +### Fixed + - Fixed issue with emphasized spaces (#146) + +## [4.6.1] +### Fixed + - Fixed conversion of `
` tags (#145)
+
+## [4.6.0]
+### Added
+ - Added support for ordered lists starting at numbers other than 1
+
+### Fixed
+ - Fixed overly-eager escaping of list-like text (#141)
+
+## [4.5.0]
+### Added
+ - Added configuration option for list item style (#135, #136)
+
+## [4.4.1]
+
+### Fixed
+ - Fixed autolinking of invalid URLs (#129)
+
+## [4.4.0]
+
+### Added
+ - Added `hard_break` configuration option (#112, #115)
+ - The `HtmlConverter` can now be instantiated with an `Environment` (#118)
+
+### Fixed
+ - Fixed handling of paragraphs in list item elements (#47, #110)
+ - Fixed phantom spaces when newlines follow `br` elements (#116, #117)
+ - Fixed link converter not sanitizing inner spaces properly (#119, #120)
+
+## [4.3.1]
+### Changed
+ - Revised the sanitization implementation (#109)
+
+### Fixed
+ - Fixed tag-like content not being escaped (#67, #109)
+ - Fixed thematic break-like content not being escaped (#65, #109)
+ - Fixed codefence-like content not being escaped (#64, #109)
+
+## [4.3.0]
+### Added
+ - Added full support for PHP 7.0 and 7.1
+
+### Changed
+ - Changed `
` and `
` conversions to use backticks instead of indendation (#102)
+
+### Fixed
+ - Fixed issue where specified code language was not preserved (#70, #102)
+ - Fixed issue where `` tags nested in `
` was not converted properly (#70, #102)
+ - Fixed header-like content not being escaped (#76, #105)
+ - Fixed blockquote-like content not being escaped (#77, #103)
+ - Fixed ordered list-like content not being escaped (#73, #106)
+ - Fixed unordered list-like content not being escaped (#71, #107)
+
+## [4.2.2]
+### Fixed
+ - Fixed sanitization bug which sometimes removes desired content (#63, #101)
+
+## [4.2.1]
+### Fixed
+ - Fixed path to autoload.php when used as a library (#98)
+ - Fixed edge case for tags containing only whitespace (#99)
+
+### Removed
+ - Removed double HTML entity decoding, as this is not desireable (#60)
+
+## [4.2.0]
+
+### Added
+ - Added the ability to invoke HtmlConverter objects as functions (#85)
+
+### Fixed
+ - Fixed improper handling of nested list items (#19 and #84)
+ - Fixed preceeding or trailing spaces within emphasis tags (#83)
+
+## [4.1.1]
+
+### Fixed
+ - Fixed conversion of empty paragraphs (#78)
+ - Fixed `preg_replace` so it wouldn't break UTF-8 characters (#79)
+
+## [4.1.0]
+
+### Added
+ - Added `bin/html-to-markdown` script
+
+### Changed
+ - Changed default italic character to `_` (#58)
+
+## [4.0.1]
+
+### Fixed
+ - Added escaping to avoid * and _ in a text being rendered as emphasis (#48)
+
+### Removed
+ - Removed the demo (#51)
+ - `.styleci.yml` and `CONTRIBUTING.md` are no longer included in distributions (#50)
+
+## [4.0.0]
+
+This release changes the visibility of several methods/properties. #42 and #43 brought to light that some visiblities were
+not ideally set, so this releases fixes that. Moving forwards this should reduce the chance of introducing BC-breaking changes.
+
+### Added
+ - Added new `HtmlConverter::getEnvironment()` method to expose the `Environment` (#42, #43)
+
+### Changed
+ - Changed `Environment::addConverter()` from `protected` to `public`, enabling custom converters to be added (#42, #43)
+ - Changed `HtmlConverter::createDOMDocument()` from `protected` to `private`
+ - Changed `Element::nextCached` from `protected` to `private`
+ - Made the `Environment` class `final`
+
+## [3.1.1]
+### Fixed
+ - Empty HTML strings now result in empty Markdown documents (#40, #41)
+
+## [3.1.0]
+### Added
+ - Added new `equals` method to `Element` to check for equality
+
+### Changes
+ - Use Linux line endings consistently instead of plaform-specific line endings (#36)
+
+### Fixed
+ - Cleaned up code style
+
+## [3.0.0]
+### Changed
+ - Changed namespace to `League\HTMLToMarkdown`
+ - Changed packagist name to `league/html-to-markdown`
+ - Re-organized code into several separate classes
+ - `` tags with identical href and inner text are now rendered using angular bracket syntax (#31)
+ - `
` elements are now treated as block-level elements (#33) + +## [2.2.2] +### Added + - Added support for PHP 5.6 and HHVM + - Enabled testing against PHP 7 nightlies + - Added this CHANGELOG.md + +### Fixed + - Fixed whitespace preservation between inline elements (#9 and #10) + +## [2.2.1] +### Fixed + - Preserve placeholder links (#22) + +## [2.2.0] +### Added + - Added CircleCI config + +### Changed + - `
` blocks are now treated as code elements
+
+### Removed
+ - Dropped support for PHP 5.2
+ - Removed incorrect README comment regarding `#text` nodes (#17)
+
+## [2.1.2]
+### Added
+ - Added the ability to blacklist/remove specific node types (#11)
+
+### Changed
+ - Line breaks are now placed after divs instead of before them
+ - Newlines inside of link texts are now removed
+ - Updated the minimum PHPUnit version to 4.*
+
+## [2.1.1]
+### Added
+ - Added options to customize emphasis characters
+
+## [2.1.0]
+### Added
+ - Added option to strip HTML tags without Markdown equivalents
+ - Added `convert()` method for converter reuse
+ - Added ability to set options after instance construction
+ - Documented the required PHP extensions (#4)
+
+### Changed
+ - ATX style now used for h1 and h2 tags inside blockquotes
+
+### Fixed
+ - Newlines inside blockquotes are now started with a bracket
+ - Fixed some incorrect docblocks
+ - `__toString()` now returns an empty string if input is empty
+ - Convert head tag if body tag is empty (#7)
+ - Preserve special characters inside tags without md equivalents (#6)
+
+
+## [2.0.1]
+### Fixed
+ - Fixed first line indentation for multi-line code blocks
+ - Fixed consecutive anchors get separating spaces stripped (#3)
+
+## [2.0.0]
+### Added
+ - Initial release
+
+[unreleased]: https://github.com/thephpleague/html-to-markdown/compare/4.8.0...master
+[4.8.0]: https://github.com/thephpleague/html-to-markdown/compare/4.7.0...4.8.0
+[4.7.0]: https://github.com/thephpleague/html-to-markdown/compare/4.6.2...4.7.0
+[4.6.2]: https://github.com/thephpleague/html-to-markdown/compare/4.6.1...4.6.2
+[4.6.1]: https://github.com/thephpleague/html-to-markdown/compare/4.6.0...4.6.1
+[4.6.0]: https://github.com/thephpleague/html-to-markdown/compare/4.5.0...4.6.0
+[4.5.0]: https://github.com/thephpleague/html-to-markdown/compare/4.4.1...4.5.0
+[4.4.1]: https://github.com/thephpleague/html-to-markdown/compare/4.4.0...4.4.1
+[4.4.0]: https://github.com/thephpleague/html-to-markdown/compare/4.3.1...4.4.0
+[4.3.1]: https://github.com/thephpleague/html-to-markdown/compare/4.3.0...4.3.1
+[4.3.0]: https://github.com/thephpleague/html-to-markdown/compare/4.2.2...4.3.0
+[4.2.2]: https://github.com/thephpleague/html-to-markdown/compare/4.2.1...4.2.2
+[4.2.1]: https://github.com/thephpleague/html-to-markdown/compare/4.2.0...4.2.1
+[4.2.0]: https://github.com/thephpleague/html-to-markdown/compare/4.1.1...4.2.0
+[4.1.1]: https://github.com/thephpleague/html-to-markdown/compare/4.1.0...4.1.1
+[4.1.0]: https://github.com/thephpleague/html-to-markdown/compare/4.0.1...4.1.0
+[4.0.1]: https://github.com/thephpleague/html-to-markdown/compare/4.0.0...4.0.1
+[4.0.0]: https://github.com/thephpleague/html-to-markdown/compare/3.1.1...4.0.0
+[3.1.1]: https://github.com/thephpleague/html-to-markdown/compare/3.1.0...3.1.1
+[3.1.0]: https://github.com/thephpleague/html-to-markdown/compare/3.0.0...3.1.0
+[3.0.0]: https://github.com/thephpleague/html-to-markdown/compare/2.2.2...3.0.0
+[2.2.2]: https://github.com/thephpleague/html-to-markdown/compare/2.2.1...2.2.2
+[2.2.1]: https://github.com/thephpleague/html-to-markdown/compare/2.2.0...2.2.1
+[2.2.0]: https://github.com/thephpleague/html-to-markdown/compare/2.1.2...2.2.0
+[2.1.2]: https://github.com/thephpleague/html-to-markdown/compare/2.1.1...2.1.2
+[2.1.1]: https://github.com/thephpleague/html-to-markdown/compare/2.1.0...2.1.1
+[2.1.0]: https://github.com/thephpleague/html-to-markdown/compare/2.0.1...2.1.0
+[2.0.1]: https://github.com/thephpleague/html-to-markdown/compare/2.0.0...2.0.1
+[2.0.0]: https://github.com/thephpleague/html-to-markdown/compare/775f91e...2.0.0
+
diff --git a/inc/html-to-markdown/CONDUCT.md b/inc/html-to-markdown/CONDUCT.md
new file mode 100755
index 0000000..01b8644
--- /dev/null
+++ b/inc/html-to-markdown/CONDUCT.md
@@ -0,0 +1,22 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic addresses, without explicit permission
+* Other unethical or unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
+
+This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
+
+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
diff --git a/inc/html-to-markdown/README.md b/inc/html-to-markdown/README.md
new file mode 100755
index 0000000..ab80541
--- /dev/null
+++ b/inc/html-to-markdown/README.md
@@ -0,0 +1,196 @@
+HTML To Markdown for PHP
+========================
+
+[![Join the chat at https://gitter.im/thephpleague/html-to-markdown](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/thephpleague/html-to-markdown?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+[![Latest Version](https://img.shields.io/packagist/v/league/html-to-markdown.svg?style=flat-square)](https://packagist.org/packages/league/html-to-markdown)
+[![Software License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
+[![Build Status](https://img.shields.io/travis/thephpleague/html-to-markdown/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/html-to-markdown)
+[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/html-to-markdown.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/html-to-markdown/code-structure)
+[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/html-to-markdown.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/html-to-markdown)
+[![Total Downloads](https://img.shields.io/packagist/dt/league/html-to-markdown.svg?style=flat-square)](https://packagist.org/packages/league/html-to-markdown)
+
+Library which converts HTML to [Markdown](http://daringfireball.net/projects/markdown/) for your sanity and convenience.
+
+
+**Requires**: PHP 5.3+ or PHP 7.0+
+
+**Lead Developer**: [@colinodell](http://twitter.com/colinodell)
+
+**Original Author**: [@nickcernis](http://twitter.com/nickcernis)
+
+
+### Why convert HTML to Markdown?
+
+*"What alchemy is this?"* you mutter. *"I can see why you'd convert [Markdown to HTML](https://github.com/thephpleague/commonmark),"* you continue, already labouring the question somewhat, *"but why go the other way?"*
+
+Typically you would convert HTML to Markdown if:
+
+1. You have an existing HTML document that needs to be edited by people with good taste.
+2. You want to store new content in HTML format but edit it as Markdown.
+3. You want to convert HTML email to plain text email. 
+4. You know a guy who's been converting HTML to Markdown for years, and now he can speak Elvish. You'd quite like to be able to speak Elvish.
+5. You just really like Markdown.
+
+### How to use it
+
+Require the library by issuing this command:
+
+```bash
+composer require league/html-to-markdown
+```
+
+Add `require 'vendor/autoload.php';` to the top of your script.
+
+Next, create a new HtmlConverter instance, passing in your valid HTML code to its `convert()` function:
+
+```php
+use League\HTMLToMarkdown\HtmlConverter;
+
+$converter = new HtmlConverter();
+
+$html = "

Quick, to the Batpoles!

"; +$markdown = $converter->convert($html); +``` + +The `$markdown` variable now contains the Markdown version of your HTML as a string: + +```php +echo $markdown; // ==> ### Quick, to the Batpoles! +``` + +The included `demo` directory contains an HTML->Markdown conversion form to try out. + +### Conversion options + +By default, HTML To Markdown preserves HTML tags without Markdown equivalents, like `` and `
`. + +To strip HTML tags that don't have a Markdown equivalent while preserving the content inside them, set `strip_tags` to true, like this: + +```php +$converter = new HtmlConverter(array('strip_tags' => true)); + +$html = 'Turnips!'; +$markdown = $converter->convert($html); // $markdown now contains "Turnips!" +``` + +Or more explicitly, like this: + +```php +$converter = new HtmlConverter(); +$converter->getConfig()->setOption('strip_tags', true); + +$html = 'Turnips!'; +$markdown = $converter->convert($html); // $markdown now contains "Turnips!" +``` + +Note that only the tags themselves are stripped, not the content they hold. + +To strip tags and their content, pass a space-separated list of tags in `remove_nodes`, like this: + +```php +$converter = new HtmlConverter(array('remove_nodes' => 'span div')); + +$html = 'Turnips!
Monkeys!
'; +$markdown = $converter->convert($html); // $markdown now contains "" +``` + +### Style options + +By default bold tags are converted using the asterisk syntax, and italic tags are converted using the underlined syntax. Change these by using the `bold_style` and `italic_style` options. + +```php +$converter = new HtmlConverter(); +$converter->getConfig()->setOption('italic_style', '*'); +$converter->getConfig()->setOption('bold_style', '__'); + +$html = 'Italic and a bold'; +$markdown = $converter->convert($html); // $markdown now contains "*Italic* and a __bold__" +``` + +### Line break options + +By default, `br` tags are converted to two spaces followed by a newline character as per [traditional Markdown](https://daringfireball.net/projects/markdown/syntax#p). Set `hard_break` to `true` to omit the two spaces, as per GitHub Flavored Markdown (GFM). + +```php +$converter = new HtmlConverter(); +$html = '

test
line break

'; + +$converter->getConfig()->setOption('hard_break', true); +$markdown = $converter->convert($html); // $markdown now contains "test\nline break" + +$converter->getConfig()->setOption('hard_break', false); // default +$markdown = $converter->convert($html); // $markdown now contains "test \nline break" +``` + +### Passing custom Environment object + +You can pass current `Environment` object to customize i.e. which converters should be used. + +```php +$environment = new Environment(array( + // your configuration here +)); +$environment->addConverter(new HeaderConverter()); // optionally - add converter manually + +$converter = new HtmlConverter($environment); + +$html = '

Header

+ +'; +$markdown = $converter->convert($html); // $markdown now contains "### Header" and "" +``` + +### Limitations + +- Markdown Extra, MultiMarkdown and other variants aren't supported – just Markdown. + +### Known issues + +- Nested lists and lists containing multiple paragraphs aren't converted correctly. +- Lists inside blockquotes aren't converted correctly. +- Any reported [open issues here](https://github.com/thephpleague/html-to-markdown/issues?state=open). + +[Report your issue or request a feature here.](https://github.com/thephpleague/html-to-markdown/issues/new) Issues with patches or failing tests are especially welcome. + +### Style notes + +- Setext (underlined) headers are the default for H1 and H2. If you prefer the ATX style for H1 and H2 (# Header 1 and ## Header 2), set `header_style` to 'atx' in the options array when you instantiate the object: + + `$converter = new HtmlConverter(array('header_style'=>'atx'));` + + Headers of H3 priority and lower always use atx style. + +- Links and images are referenced inline. Footnote references (where image src and anchor href attributes are listed in the footnotes) are not used. +- Blockquotes aren't line wrapped – it makes the converted Markdown easier to edit. + +### Dependencies + +HTML To Markdown requires PHP's [xml](http://www.php.net/manual/en/xml.installation.php), [lib-xml](http://www.php.net/manual/en/libxml.installation.php), and [dom](http://www.php.net/manual/en/dom.installation.php) extensions, all of which are enabled by default on most distributions. + +Errors such as "Fatal error: Class 'DOMDocument' not found" on distributions such as CentOS that disable PHP's xml extension can be resolved by installing php-xml. + +### Contributors + +Many thanks to all [contributors](https://github.com/thephpleague/html-to-markdown/graphs/contributors) so far. Further improvements and feature suggestions are very welcome. + +### How it works + +HTML To Markdown creates a DOMDocument from the supplied HTML, walks through the tree, and converts each node to a text node containing the equivalent markdown, starting from the most deeply nested node and working inwards towards the root node. + +### To-do + +- Support for nested lists and lists inside blockquotes. +- Offer an option to preserve tags as HTML if they contain attributes that can't be represented with Markdown (e.g. `style`). + +### Trying to convert Markdown to HTML? + +Use one of these great libraries: + + - [league/commonmark](https://github.com/thephpleague/commonmark) (recommended) + - [cebe/markdown](https://github.com/cebe/markdown) + - [PHP Markdown](https://michelf.ca/projects/php-markdown/) + - [Parsedown](https://github.com/erusev/parsedown) + +No guarantees about the Elvish, though. + diff --git a/inc/html-to-markdown/composer.json b/inc/html-to-markdown/composer.json new file mode 100755 index 0000000..5340391 --- /dev/null +++ b/inc/html-to-markdown/composer.json @@ -0,0 +1,48 @@ +{ + "name": "league/html-to-markdown", + "type": "library", + "description": "An HTML-to-markdown conversion helper for PHP", + "keywords": ["markdown", "html"], + "homepage": "https://github.com/thephpleague/html-to-markdown", + "license": "MIT", + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "League\\HTMLToMarkdown\\Test\\": "tests" + } + }, + "require": { + "php": ">=5.3.3", + "ext-dom": "*", + "ext-xml": "*" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "~1.1.0", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1" + }, + "bin": ["bin/html-to-markdown"], + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + } +} diff --git a/inc/html-to-markdown/src/Configuration.php b/inc/html-to-markdown/src/Configuration.php new file mode 100755 index 0000000..5bc8d55 --- /dev/null +++ b/inc/html-to-markdown/src/Configuration.php @@ -0,0 +1,76 @@ +config = $config; + + $this->checkForDeprecatedOptions($config); + } + + /** + * @param array $config + */ + public function merge(array $config = array()) + { + $this->checkForDeprecatedOptions($config); + $this->config = array_replace_recursive($this->config, $config); + } + + /** + * @param array $config + */ + public function replace(array $config = array()) + { + $this->checkForDeprecatedOptions($config); + $this->config = $config; + } + + /** + * @param string $key + * @param mixed $value + */ + public function setOption($key, $value) + { + $this->checkForDeprecatedOptions(array($key => $value)); + $this->config[$key] = $value; + } + + /** + * @param string|null $key + * @param mixed|null $default + * + * @return mixed|null + */ + public function getOption($key = null, $default = null) + { + if ($key === null) { + return $this->config; + } + + if (!isset($this->config[$key])) { + return $default; + } + + return $this->config[$key]; + } + + private function checkForDeprecatedOptions(array $config) + { + foreach ($config as $key => $value) { + if ($key === 'bold_style' && $value !== '**') { + @trigger_error('Customizing the bold_style option is deprecated and may be removed in the next major version', E_USER_DEPRECATED); + } elseif ($key === 'italic_style' && $value !== '*') { + @trigger_error('Customizing the italic_style option is deprecated and may be removed in the next major version', E_USER_DEPRECATED); + } + } + } +} diff --git a/inc/html-to-markdown/src/ConfigurationAwareInterface.php b/inc/html-to-markdown/src/ConfigurationAwareInterface.php new file mode 100755 index 0000000..8aca530 --- /dev/null +++ b/inc/html-to-markdown/src/ConfigurationAwareInterface.php @@ -0,0 +1,11 @@ +' symbols to each line. + + $markdown = ''; + + $quote_content = trim($element->getValue()); + + $lines = preg_split('/\r\n|\r|\n/', $quote_content); + + $total_lines = count($lines); + + foreach ($lines as $i => $line) { + $markdown .= '> ' . $line . "\n"; + if ($i + 1 === $total_lines) { + $markdown .= "\n"; + } + } + + return $markdown; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('blockquote'); + } +} diff --git a/inc/html-to-markdown/src/Converter/CodeConverter.php b/inc/html-to-markdown/src/Converter/CodeConverter.php new file mode 100755 index 0000000..39e6a7b --- /dev/null +++ b/inc/html-to-markdown/src/Converter/CodeConverter.php @@ -0,0 +1,79 @@ +getAttribute('class'); + + if ($classes) { + // Since tags can have more than one class, we need to find the one that starts with 'language-' + $classes = explode(' ', $classes); + foreach ($classes as $class) { + if (strpos($class, 'language-') !== false) { + // Found one, save it as the selected language and stop looping over the classes. + $language = str_replace('language-', '', $class); + break; + } + } + } + + $markdown = ''; + $code = html_entity_decode($element->getChildrenAsString()); + + // In order to remove the code tags we need to search for them and, in the case of the opening tag + // use a regular expression to find the tag and the other attributes it might have + $code = preg_replace('/]*>/', '', $code); + $code = str_replace('
', '', $code); + + // Checking if it's a code block or span + if ($this->shouldBeBlock($element, $code)) { + // Code block detected, newlines will be added in parent + $markdown .= '```' . $language . "\n" . $code . "\n" . '```'; + } else { + // One line of code, wrapping it on one backtick, removing new lines + $markdown .= '`' . preg_replace('/\r\n|\r|\n/', '', $code) . '`'; + } + + return $markdown; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('code'); + } + + /** + * @param ElementInterface $element + * @param string $code + * + * @return bool + */ + private function shouldBeBlock(ElementInterface $element, $code) + { + if ($element->getParent()->getTagName() == 'pre') { + return true; + } + + if (preg_match('/[^\s]` `/', $code)) { + return true; + } + + return false; + } +} diff --git a/inc/html-to-markdown/src/Converter/CommentConverter.php b/inc/html-to-markdown/src/Converter/CommentConverter.php new file mode 100755 index 0000000..55038b2 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/CommentConverter.php @@ -0,0 +1,26 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + // If strip_tags is false (the default), preserve tags that don't have Markdown equivalents, + // such as nodes on their own. C14N() canonicalizes the node to a string. + // See: http://www.php.net/manual/en/domnode.c14n.php + if ($this->config->getOption('strip_tags', false)) { + return $element->getValue(); + } + + return html_entity_decode($element->getChildrenAsString()); + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array(self::DEFAULT_CONVERTER); + } +} diff --git a/inc/html-to-markdown/src/Converter/DivConverter.php b/inc/html-to-markdown/src/Converter/DivConverter.php new file mode 100755 index 0000000..656a0ba --- /dev/null +++ b/inc/html-to-markdown/src/Converter/DivConverter.php @@ -0,0 +1,45 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + if ($this->config->getOption('strip_tags', false)) { + return $element->getValue() . "\n\n"; + } + + return html_entity_decode($element->getChildrenAsString()); + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('div'); + } +} diff --git a/inc/html-to-markdown/src/Converter/EmphasisConverter.php b/inc/html-to-markdown/src/Converter/EmphasisConverter.php new file mode 100755 index 0000000..8fd4dd6 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/EmphasisConverter.php @@ -0,0 +1,57 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + $tag = $element->getTagName(); + $value = $element->getValue(); + + if (!trim($value)) { + return $value; + } + + if ($tag === 'i' || $tag === 'em') { + $style = $this->config->getOption('italic_style'); + } else { + $style = $this->config->getOption('bold_style'); + } + + $prefix = ltrim($value) !== $value ? ' ' : ''; + $suffix = rtrim($value) !== $value ? ' ' : ''; + + return $prefix . $style . trim($value) . $style . $suffix; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('em', 'i', 'strong', 'b'); + } +} diff --git a/inc/html-to-markdown/src/Converter/HardBreakConverter.php b/inc/html-to-markdown/src/Converter/HardBreakConverter.php new file mode 100755 index 0000000..d079d91 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/HardBreakConverter.php @@ -0,0 +1,53 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + $return = $this->config->getOption('hard_break') ? "\n" : " \n"; + + $next = $element->getNext(); + if ($next) { + $next_value = $next->getValue(); + if ($next_value) { + if (in_array(substr($next_value, 0, 2), array('- ', '* ', '+ '))) { + $return .= '\\'; + } + } + } + + return $return; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('br'); + } +} diff --git a/inc/html-to-markdown/src/Converter/HeaderConverter.php b/inc/html-to-markdown/src/Converter/HeaderConverter.php new file mode 100755 index 0000000..05d4fe8 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/HeaderConverter.php @@ -0,0 +1,82 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + $level = (int) substr($element->getTagName(), 1, 1); + $style = $this->config->getOption('header_style', self::STYLE_SETEXT); + + if (strlen($element->getValue()) === 0) { + return ''; + } + + if (($level === 1 || $level === 2) && !$element->isDescendantOf('blockquote') && $style === self::STYLE_SETEXT) { + return $this->createSetextHeader($level, $element->getValue()); + } + + return $this->createAtxHeader($level, $element->getValue()); + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); + } + + /** + * @param int $level + * @param string $content + * + * @return string + */ + private function createSetextHeader($level, $content) + { + $length = function_exists('mb_strlen') ? mb_strlen($content, 'utf-8') : strlen($content); + $underline = ($level === 1) ? '=' : '-'; + + return $content . "\n" . str_repeat($underline, $length) . "\n\n"; + } + + /** + * @param int $level + * @param string $content + * + * @return string + */ + private function createAtxHeader($level, $content) + { + $prefix = str_repeat('#', $level) . ' '; + + return $prefix . $content . "\n\n"; + } +} diff --git a/inc/html-to-markdown/src/Converter/HorizontalRuleConverter.php b/inc/html-to-markdown/src/Converter/HorizontalRuleConverter.php new file mode 100755 index 0000000..8f54f93 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/HorizontalRuleConverter.php @@ -0,0 +1,26 @@ +getAttribute('src'); + $alt = $element->getAttribute('alt'); + $title = $element->getAttribute('title'); + + if ($title !== '') { + // No newlines added. should be in a block-level element. + return '![' . $alt . '](' . $src . ' "' . $title . '")'; + } + + return '![' . $alt . '](' . $src . ')'; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('img'); + } +} diff --git a/inc/html-to-markdown/src/Converter/LinkConverter.php b/inc/html-to-markdown/src/Converter/LinkConverter.php new file mode 100755 index 0000000..c82b70e --- /dev/null +++ b/inc/html-to-markdown/src/Converter/LinkConverter.php @@ -0,0 +1,65 @@ +getAttribute('href'); + $title = $element->getAttribute('title'); + $text = trim($element->getValue(), "\t\n\r\0\x0B"); + + if ($title !== '') { + $markdown = '[' . $text . '](' . $href . ' "' . $title . '")'; + } elseif ($href === $text && $this->isValidAutolink($href)) { + $markdown = '<' . $href . '>'; + } elseif ($href === 'mailto:' . $text && $this->isValidEmail($text)) { + $markdown = '<' . $text . '>'; + } else { + $markdown = '[' . $text . '](' . $href . ')'; + } + + if (!$href) { + $markdown = html_entity_decode($element->getChildrenAsString()); + } + + return $markdown; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('a'); + } + + /** + * @param string $href + * + * @return bool + */ + private function isValidAutolink($href) + { + return preg_match('/^[A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]*/i', $href) === 1; + } + + /** + * @param string $email + * + * @return bool + */ + private function isValidEmail($email) + { + // Email validation is messy business, but this should cover most cases + return filter_var($email, FILTER_VALIDATE_EMAIL); + } +} diff --git a/inc/html-to-markdown/src/Converter/ListBlockConverter.php b/inc/html-to-markdown/src/Converter/ListBlockConverter.php new file mode 100755 index 0000000..07a4c85 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/ListBlockConverter.php @@ -0,0 +1,26 @@ +getValue() . "\n"; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('ol', 'ul'); + } +} diff --git a/inc/html-to-markdown/src/Converter/ListItemConverter.php b/inc/html-to-markdown/src/Converter/ListItemConverter.php new file mode 100755 index 0000000..c56ab89 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/ListItemConverter.php @@ -0,0 +1,81 @@ +config = $config; + } + + /** + * @param ElementInterface $element + * + * @return string + */ + public function convert(ElementInterface $element) + { + // If parent is an ol, use numbers, otherwise, use dashes + $list_type = $element->getParent()->getTagName(); + + // Add spaces to start for nested list items + $level = $element->getListItemLevel($element); + + $prefixForParagraph = str_repeat(' ', $level + 1); + $value = trim(implode("\n" . $prefixForParagraph, explode("\n", trim($element->getValue())))); + + // If list item is the first in a nested list, add a newline before it + $prefix = ''; + if ($level > 0 && $element->getSiblingPosition() === 1) { + $prefix = "\n"; + } + + if ($list_type === 'ul') { + $list_item_style = $this->config->getOption('list_item_style', '-'); + $list_item_style_alternate = $this->config->getOption('list_item_style_alternate'); + if (!isset($this->listItemStyle)) { + $this->listItemStyle = $list_item_style_alternate ? $list_item_style_alternate : $list_item_style; + } + + if ($list_item_style_alternate && $level == 0 && $element->getSiblingPosition() === 1) { + $this->listItemStyle = $this->listItemStyle == $list_item_style ? $list_item_style_alternate : $list_item_style; + } + + return $prefix . $this->listItemStyle . ' ' . $value . "\n"; + } + + if ($list_type === 'ol' && $start = $element->getParent()->getAttribute('start')) { + $number = $start + $element->getSiblingPosition() - 1; + } else { + $number = $element->getSiblingPosition(); + } + + return $prefix . $number . '. ' . $value . "\n"; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('li'); + } +} diff --git a/inc/html-to-markdown/src/Converter/ParagraphConverter.php b/inc/html-to-markdown/src/Converter/ParagraphConverter.php new file mode 100755 index 0000000..7207b81 --- /dev/null +++ b/inc/html-to-markdown/src/Converter/ParagraphConverter.php @@ -0,0 +1,125 @@ +getValue(); + + $markdown = ''; + + $lines = preg_split('/\r\n|\r|\n/', $value); + foreach ($lines as $line) { + /* + * Some special characters need to be escaped based on the position that they appear + * The following function will deal with those special cases. + */ + $markdown .= $this->escapeSpecialCharacters($line); + $markdown .= "\n"; + } + + return trim($markdown) !== '' ? rtrim($markdown) . "\n\n" : ''; + } + + /** + * @return string[] + */ + public function getSupportedTags() + { + return array('p'); + } + + /** + * @param string $line + * + * @return string + */ + private function escapeSpecialCharacters($line) + { + $line = $this->escapeFirstCharacters($line); + $line = $this->escapeOtherCharacters($line); + $line = $this->escapeOtherCharactersRegex($line); + + return $line; + } + + /** + * @param string $line + * + * @return string + */ + private function escapeFirstCharacters($line) + { + $escapable = array( + '>', + '- ', + '+ ', + '--', + '~~~', + '---', + '- - -' + ); + + foreach ($escapable as $i) { + if (strpos(ltrim($line), $i) === 0) { + // Found a character that must be escaped, adding a backslash before + return '\\' . ltrim($line); + } + } + + return $line; + } + + /** + * @param string $line + * + * @return string + */ + private function escapeOtherCharacters($line) + { + $escapable = array( + ' + . + /vendor/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ec9429e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +sudo: false +dist: trusty + +language: php + +notifications: + email: + on_success: never + on_failure: change + +branches: + only: + - master + +cache: + directories: + - $HOME/.composer/cache + +matrix: + include: + - php: 7.2 + env: WP_VERSION=latest + - php: 7.1 + env: WP_VERSION=latest + - php: 7.0 + env: WP_VERSION=latest + - php: 5.6 + env: WP_VERSION=latest + - php: 5.6 + env: WP_VERSION=trunk + - php: 5.6 + env: WP_TRAVISCI=phpcs + - php: 5.3 + env: WP_VERSION=latest + dist: precise + +before_script: + - export PATH="$HOME/.composer/vendor/bin:$PATH" + - | + if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then + phpenv config-rm xdebug.ini + else + echo "xdebug.ini does not exist" + fi + - | + if [[ ! -z "$WP_VERSION" ]] ; then + bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION + composer global require "phpunit/phpunit=4.8.*|5.7.*" + fi + - | + if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then + composer global require wp-coding-standards/wpcs + phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs + fi + +script: + - | + if [[ ! -z "$WP_VERSION" ]] ; then + phpunit + WP_MULTISITE=1 phpunit + fi + - | + if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then + phpcs + fi diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 0000000..364f839 --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi + +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i.bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_wp +install_test_suite +install_db diff --git a/classes/lbrypress.php b/classes/lbrypress.php index d8abbb3..b087fae 100644 --- a/classes/lbrypress.php +++ b/classes/lbrypress.php @@ -176,8 +176,6 @@ class LBRYPress // } // } // update_option(LBRY_SETTINGS, $new_settings); - - error_log('Activated'); } /** diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d9af975 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,15 @@ + + + + + ./tests/ + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..3318bf8 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,31 @@ +class_instance = LBRYPress::instance(); + } + + public function test_init() + { + // Init is called during constructor + $this->assertInstanceOf(LBRY_Daemon::class, $this->class_instance->daemon); + $this->assertInstanceOf(LBRY_Speech::class, $this->class_instance->speech); + $this->assertInstanceOf(LBRY_Admin_Notice::class, $this->class_instance->notice); + } + + /** + * @depends test_init + * Test activation hook + */ + public function test_activate() + { + // Make sure we have options when activated + $this->class_instance->activate(); + $this->assertTrue(!empty(get_option(LBRY_SETTINGS))); + } +} From 1e9b37f9703ee70b95e174526d2ca4fd8abe3d5f Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Thu, 11 Oct 2018 15:59:55 -0500 Subject: [PATCH 07/13] Testing network parser --- tests/test-lbry_network_parser.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test-lbry_network_parser.php b/tests/test-lbry_network_parser.php index e69de29..279b9c1 100644 --- a/tests/test-lbry_network_parser.php +++ b/tests/test-lbry_network_parser.php @@ -0,0 +1,23 @@ +class_instance = new LBRY_Network_Parser(); + } + + public function test_convert_to_markdown() + { + } +} From 04381099216e17189100827e3bd372f903f999a0 Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Thu, 11 Oct 2018 18:55:43 -0500 Subject: [PATCH 08/13] Created test for converting post to Markdown --- classes/LBRY_Network_Parser.php | 5 +-- tests/convert_actual.md | 56 ++++++++++++++++++++++++++++++ tests/convert_content.txt | 33 ++++++++++++++++++ tests/convert_expected.md | 56 ++++++++++++++++++++++++++++++ tests/convert_initial.html | 0 tests/test-lbry_network_parser.php | 15 ++++++++ 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 tests/convert_actual.md create mode 100644 tests/convert_content.txt delete mode 100644 tests/convert_initial.html diff --git a/classes/LBRY_Network_Parser.php b/classes/LBRY_Network_Parser.php index 5847de4..4ac3fad 100644 --- a/classes/LBRY_Network_Parser.php +++ b/classes/LBRY_Network_Parser.php @@ -20,7 +20,6 @@ class LBRY_Network_Parser $this->converter = new HtmlConverter(array( 'strip_tags' => true )); - add_action('save_post', array($this, 'convert_to_markdown')); } public function convert_to_markdown($post_id) @@ -31,7 +30,9 @@ class LBRY_Network_Parser $featured_image = get_the_post_thumbnail($post); $content = $title; - $content .= $featured_image . '
'; + if ($featured_image) { + $content .= $featured_image . '
'; + } $content .= apply_filters('the_content', get_post($post_id)->post_content); $converted = $this->converter->convert($content); diff --git a/tests/convert_actual.md b/tests/convert_actual.md new file mode 100644 index 0000000..f81bb84 --- /dev/null +++ b/tests/convert_actual.md @@ -0,0 +1,56 @@ +Markdown Test! +============== + +**([FAIR.org](https://fair.org/home/trump-admin-follows-corporate-media-playbook-for-war-with-iran/))** — Three years ago, as Americans debated the Joint Comprehensive Plan of Action (JCPOA) agreement with the Islamic Republic of Iran—popularly known as “the Iran deal”—I highlighted a troubling media trend on **FAIR.org** ([8/20/15](https://fair.org/home/giving-war-a-chance/)): “For nearly all commentators, regardless of their position, war is the only alternative to that position.” + +In the months since US President Donald Trump tore up the JCPOA agreement, his administration has been trying to make good on corporate media’s collective prediction. Last week, John Bolton (**BBC**, [9/26/18](https://www.bbc.com/news/world-us-canada-45647863)), Trump’s national security advisor and chief warmonger, told Iran’s leaders and the world that there would be “hell to pay” if they dare to “cross us.” + +![](https://theantimedia.com/wp-content/uploads/2018/10/BBC-John-Bolton-610x343.jpg)John Bolton (**BBC**, [9/26/18](https://www.bbc.com/news/world-us-canada-45647863)): “Let my message today be clear: We are watching, and we will come after you.”That Bolton’s bellicose statements do not send shockwaves of pure horror across a [debt-strapped](https://www.reuters.com/article/us-usa-economy-budget/u-s-government-posts-214-billion-deficit-in-august-idUSKCN1LT2XL) and [war-weary](https://nyti.ms/2N7cg3m) United States is thanks in large part to incessant priming for war, facilitated by corporate media across the entire political spectrum, with a particular focus on Iran. + +Back in 2015, while current “resistance” stalwarts like the **Washington Post** ([4/2/15](https://www.washingtonpost.com/opinions/a-nuclear-deal-with-iran-is-the-best-option/2015/04/02/bc8292d2-d978-11e4-8103-fa84725dbf9d_story.html)) and **Politico** ([8/11/15](//www.politico.com/magazine/story/2015/08/iran-deal-rejection-121257.html)) warned us that war with Iran was the most likely alternative to the JCPOA, conservative standard-bearers such as **Fox News** ([7/14/15](//insider.foxnews.com/2015/07/14/dick-cheney-hannity-iran-nuke-deal-brings-world-closer-nuclear-war)) and the **Washington Times** ([8/10/15](//www.washingtontimes.com/news/2015/aug/10/ed-feulner-iran-nuclear-deal-makes-war-more-likely/)) foretold that war with Iran was the agreement’s most likely outcome. Three years hence, this dynamic has not changed. + +![](https://theantimedia.com/wp-content/uploads/2018/10/NYT-Iran-Deal-War-610x411.jpg)Cartoonist Patrick Chappatte (**New York Times**, [5/10/18](https://nyti.ms/2I9LCnJ)) presents Trump and Bolton’s “deal” for Iran.To experience the full menu of US media’s single-mindedness about Iran, one need only buy a subscription to the **New York Times**. After Trump withdrew from the JCPOA, the **Times**’ editorial board ([5/8/18](https://nyti.ms/2HXldJD)) wrote that his move would “lay conditions for a possible wider war in the Middle East.” Susan Rice (**New York Times**, [5/8/18](https://nyti.ms/2FXt4kH)), President Barack Obama’s national security advisor, agreed: “We could face the choice of going to war or acquiescing to a nuclear-armed Iran,” she warned. Cartoonist Patrick Chappatte (**New York Times**, [5/10/18](https://nyti.ms/2I9LCnJ)) was characteristically more direct, penning an image of Trump alongside Bolton, holding a fictitious new agreement featuring the singular, ultimate word: “WAR.” + +On the other hand, calling Trump’s turn against JCPOA a “courageous decision,” **Times** columnist Bret Stephens ([5/8/18](https://nyti.ms/2FWTz9O)) explained that the move was meant to force the Iranian government to make a choice: Either accede to US demands or “pursue their nuclear ambitions at the cost of economic ruin and possible war.” (Hardly courageous, when we all know there is no chance that Trump or Stephens would enlist should war materialize.) + +Trump’s [latest antics](https://youtu.be/KfVdIKaQzW8) at the United Nations have spurred a wave of similar reaction across corporate media. Describing his threat to “totally destroy North Korea” at the UN General Assembly last year as “pointed and sharp,” **Fox News** anchor Eric Shawn ([9/23/18](https://video.foxnews.com/v/5838934710001/)) asked Bill Richardson, an Obama ally and President Bill Clinton’s ambassador to the UN, whether Trump would take the same approach toward Iran. “That aggressive policy we have with Iran is going to continue,” Richardson reassured the audience, “and I don’t think Iran is helping themselves.” In other words, if the United States starts a war with Iran, it’s totally Iran’s fault. + +**Politico** ([9/23/18](https://www.politico.com/story/2018/09/23/trump-iran-war-foreign-policy-836411)), meanwhile, reported that Trump “is risking a potential war with Iran unless he engages the Islamist-led country using diplomacy.” In other words, if the United States starts a war with Iran, it’s totally Trump’s fault. Rice (**New York Times**, [9/26/18](https://nyti.ms/2N4AZjF)) reiterated her view that Trump’s rhetoric “presages the prospect of war in the Persian Gulf.” Whoever would be the responsible party is up for debate, but that war is in our future is apparently all but certain. + +**Politico**’s article cited a [statement](https://s3-us-west-1.amazonaws.com/coalitionagainstirannukes/Statement_Sept18.pdf) signed by such esteemed US experts on war-making as Madeleine Albright, who presided over Clinton’s [inhuman sanctions](https://nyti.ms/2tvy0MU) against Iraq in the ’90s, and Ryan Crocker, former ambassador for presidents George W. Bush and Obama to some of America’s favorite killing fields: Iraq, Afghanistan, Pakistan and Syria. James Clapper, Obama’s National Intelligence Director, who also signed the letter, played an [important role](https://www.washingtontimes.com/news/2010/jun/4/likely-intel-chief-clapper-held-disputed-wmd-view/) in trumping up WMD evidence against Saddam Hussein before the United States invaded Iraq in 2003. When it comes to US aggression, they’re the experts. + +**Vanity Fair** ([9/26/18](https://www.vanityfair.com/news/2018/09/john-bolton-iran-united-nations)) interviewed John Glaser of the Cato Institute, who called Trump’s strategy “pathetic,” and also warned that it forebodes war. In an effort to “one-up Obama,” Glaser explained, Trump’s plan is “to apply extreme economic pressure and explicit threats of war in order to get Iran to capitulate.” Sound familiar? As Glaser implies, this was [exactly Obama’s strategy](https://theintercept.com/2015/08/06/obama-summarizes-record/), only then it wasn’t seen as “pathetic,” but rather reasonable, and the sole means for preventing the war that every US pundit and politician saw around the corner (**The Hill**, [8/9/15](https://thehill.com/blogs/ballot-box/presidential-races/250683-sanders-war-the-alternative-if-iran-deal-fails)). + +When everyone decides that war is the only other possibility, it starts to look like an inevitability. But even when they aren’t overtly stoking war fever against Iran, corporate media prime the militaristic pump in more subtle yet equally disturbing ways. + +![](https://theantimedia.com/wp-content/uploads/2018/10/CNN-Netanyahu-610x343.jpg)Benjamin Netanyahu speaks for the Iranian people on **CNN** ([9/29/18](https://www.cnn.com/videos/world/2018/09/29/labott-netanyahu-iran-regime-intv-sot-vpx.cnn)).First among these is the near-complete erasure of Iranian voices from US airwaves (**FAIR.org**, [7/24/15](https://fair.org/home/side-by-side-coverage-of-cuba-and-iran-highlights-shift-in-us-media-villain-making/)). Rather than ask Iranians directly, national outlets like **CNN** ([9/29/18](https://www.cnn.com/videos/world/2018/09/29/labott-netanyahu-iran-regime-intv-sot-vpx.cnn)) prefer to invite the prime minister of Israel, [serial Iran alarmist](https://theintercept.com/2015/03/02/brief-history-netanyahu-crying-wolf-iranian-nuclear-bomb/) and regional pariah Benjamin Netanyahu, to speak for them. During a jovial discussion this weekend over whether regime change and/or economic collapse is Iran’s most likely fate, Netanyahu explained to the audience that, either way, “The ones who will be happiest if that happens are the people of Iran.” No people of Iran were on hand to confirm or deny this assessment. + +**Bloomberg** ([9/30/18](https://www.bloomberg.com/view/articles/2018-09-30/what-s-not-to-like-about-trump-iran-oil-sanctions-100-oil)) similarly wanted to know, “What’s not to like about Trump’s Iran oil sanctions?” Julian Lee gleefully reported that “they are crippling exports from the Islamic Republic, at minimal cost to the US.” One might think the [toll sanctions take](https://www.aljazeera.com/news/2018/08/iran-doctors-sanctions-endangering-patients-lives-180830064012071.html) on innocent Iranians would be something not to like, but **Bloomberg** merely worried that, notwithstanding the windfall for US refineries, “oil at $100 a barrel would be bad news for drivers everywhere—including those in the US.” + +Another prized tactic is to [whitewash Saudi Arabia](https://www.counterpunch.org/2018/03/05/the-1-5-billion-campaign-to-whitewash-genocide-in-yemen/), Iran’s chief geopolitical rival, whose genocidal destruction of Yemen is made possible by the United States, about which corporate media remain overwhelmingly silent (**FAIR.org**,[ 7/23/18](https://fair.org/home/action-alert-its-been-over-a-year-since-msnbc-has-mentioned-us-war-in-yemen/)). Iran’s involvement in Yemen, which both Trump and the **New York Times** ([9/12/18](https://nyti.ms/2MqieXJ)) describe as “malign behavior,” is a principal justification for US support of Saudi Arabia, including the[ US-supplied bombs](https://www.cnn.com/2018/08/17/middleeast/us-saudi-yemen-bus-strike-intl/index.html) that recently ended the brief lives of over 40 Yemeni schoolchildren. Lockheed Martin’s stock is [up 34 percent](https://www.marketwatch.com/investing/stock/LMT/historical?siteid=mktw&date=01%2F20%2F2017)from Trump’s inauguration day. + +Corporate media go beyond a simple coverup of Saudi crimes to evangelize their leadership as the liberal antidote to Iran’s “theocracy.” Who can forget Thomas Friedman’s revolting puff piece for the Saudi crown prince Mohammad bin Salman? Extensively quoting Salman (**New York Times**, [11/23/17](https://nyti.ms/2i0mwfg)), who refers to Iranian Ayatollah Ali Khamenei as “the new Hitler of the Middle East,” Friedman nevertheless remains pessimistic about whether “MBS and his team” can see their stand against Iran through, as “dysfunction and rivalries within the Sunni Arab world generally have prevented forming a unified front.” Oh well, every team needs cheerleaders, and Friedman isn’t just a fair-weather fan. + +While Friedman (**New York Times**, [5/15/18](https://nyti.ms/2GmBgLo)) believes that Trump has drawn “some needed attention to Iran’s bad behavior,” for him pivotal questions remain unanswered, such as “who is going to take over in Tehran if the current Islamic regime collapses?” One immediate fix he proposed was to censure Iran’s metaphorical “occupation” of Syria, Iraq and Lebanon. Isn’t this ironic coming from an unapologetic propagandist for Washington’s decades-long, non-metaphorical occupation of the two countries to the east and west of Iran (**FAIR.org**, [12/9/15](https://fair.org/home/friedman-goes-after-trump-hey-massive-bombing-was-my-idea/))? + +In a surprising break from corporate media convention, **USA Today** ([9/26/18](https://www.usatoday.com/story/opinion/voices/2018/09/26/iran-united-states-foreign-policy-iraq-war-column/1379872002/)) published a column on US/Iran relations written by an actual Iranian. Reflecting on the [CIA-orchestrated coup](https://en.wikipedia.org/wiki/1953_Iranian_coup_d%27%C3%A9tat) against Iran’s elected government in 1953, Azadeh Shahshahani, who was born four days after the 1979 revolution there, wrote: + +> “I often wonder what would have happened if that coup had not worked, if \[Prime Minister\] Mosaddeq had been allowed to govern, if democracy had been allowed to flourish.” + +“It is time for the US government to stop intervening in Iran and let the Iranian people determine their own destiny,” she beseeched readers. + +![](https://theantimedia.com/wp-content/uploads/2018/10/Real-News-Code-Pink-610x343.png)Code Pink’s Medea Benjamin confronts the head of Trump’s “Iran Action Group” (**Real News**, [9/21/18](https://youtu.be/peJGmeg6ZE8)).Shahshahani’s call is supported by some who have rejected corporate media’s war propaganda and have gone to extreme lengths to have their perspectives heard. Anti-war activist and Code Pink founder Medea Benjamin was recently forcibly removed after she upstaged Brian Hook, leader of Trump’s Iran Action Group, on live TV, calling his press conference “the most ridiculous thing I have ever seen” (**Real News**, [9/21/18](https://youtu.be/peJGmeg6ZE8)). Benjamin implored the audience: “Let’s talk about Saudi Arabia. Is that who our allies are?” + +“How dare you bring up the issue of Yemen,” admonished Benjamin as she was dragged from the room. “It’s the Saudi bombing that is killing most people in Yemen. So let’s get real. No more war! Peace with Iran!” Code Pink is [currently petitioning](https://www.codepink.org/dont_iraq_iran?utm_campaign=iran_national_sept25) the **New York Times** and **Washington Post** to stop propagandizing war. + +Sadly, no matter whom you ask in corporate media, be they spokespeople for “Trump’s America” or “the resistance,” peace remains an elusive choice in the US political imagination. And while the public was focused last week on Supreme Court nominee [Brett Kavanaugh’s ](https://youtu.be/7zVOkb3CdZ0)[perjurious testimony](https://www.currentaffairs.org/2018/09/how-we-know-kavanaugh-is-lying), the Senate finalized a[$674 billion “defense” budget](https://www.democracynow.org/2018/9/20/headlines/senate_passes_674_billion_military_spending_bill). Every single Democrat in the chamber voted in favor of the bill, [explicitly naming Iran](https://www.congress.gov/bill/115th-congress/house-bill/6157/text) as persona non grata in the United States’[world-leading arms supply network](https://www.sipri.org/news/press-release/2018/asia-and-middle-east-lead-rising-trend-arms-imports-us-exports-grow-significantly-says-sipri), which has seen a 25 percent increase in exports since Obama took office in 2009. + +The US government’s imperial ambitions are perhaps its only truly bipartisan project—what the **New York Times** euphemistically refers to as “globalism.” Nowhere was this on fuller display than at the funeral for Republican Sen. John McCain (**FAIR.org**,[ 9/11/18](https://fair.org/home/maverick-media-use-mccain-funeral-to-shore-up-us-imperialism/)), where politicians of all stripes were tripping over themselves to produce the best accolades for a man who [infamously sang](https://youtu.be/o-zoPgv_nYg)“bomb bomb bomb, bomb bomb Iran” to the tune of a Beach Boys song. + +McCain’s bloodlust was nothing new. Nearly a hundred years ago, after the West’s imperial competition culminated in the most destructive war the world had ever seen, the brilliant American sociologist and anti-colonial author WEB Du Bois [wrote](https://www.gutenberg.org/files/15210/15210-h/15210-h.htm), “This is not Europe gone mad; this is not aberration nor insanity; this *is* Europe.” + +Iranian leaders have repeatedly said they do not want war with the US (**AP**, [9/27/18](https://apnews.com/1cb23ee1641041c5910595715f2ea334)), but US corporate media, despite frequently characterizing Trump as a “mad king” (**FAIR.org**, [6/13/18](https://fair.org/home/why-do-us-media-only-worry-about-one-authoritarians-nukes/)), continue to play an instrumental role in rationalizing a future war with Iran. Should such an intentional catastrophe come to pass, we can hardly say that this would be America gone mad; war is not aberration, it is always presented as the next sane choice. This *is* America. + +*By [John C. O’Day](https://fair.org/author/john-c-oday/) / Republished with permission / [FAIR.org](https://fair.org/) / [Report a typo](mailto:edits@theantimedia.org)* + +*This article was chosen for republication based on the interest of our readers. Anti-Media republishes stories from a number of other independent news sources. The views expressed in this article are the author’s own and do not reflect Anti-Media editorial policy.* \ No newline at end of file diff --git a/tests/convert_content.txt b/tests/convert_content.txt new file mode 100644 index 0000000..20f30b4 --- /dev/null +++ b/tests/convert_content.txt @@ -0,0 +1,33 @@ +

(FAIR.org— Three years ago, as Americans debated the Joint Comprehensive Plan of Action (JCPOA) agreement with the Islamic Republic of Iran—popularly known as “the Iran deal”—I highlighted a troubling media trend on FAIR.org (8/20/15): “For nearly all commentators, regardless of their position, war is the only alternative to that position.”

+

In the months since US President Donald Trump tore up the JCPOA agreement, his administration has been trying to make good on corporate media’s collective prediction. Last week, John Bolton (BBC9/26/18), Trump’s national security advisor and chief warmonger, told Iran’s leaders and the world that there would be “hell to pay” if they dare to “cross us.”

+
John Bolton (BBC, 9/26/18): “Let my message today be clear: We are watching, and we will come after you.”
+

That Bolton’s bellicose statements do not send shockwaves of pure horror across a debt-strapped and war-weary United States is thanks in large part to incessant priming for war, facilitated by corporate media across the entire political spectrum, with a particular focus on Iran.

+

Back in 2015, while current “resistance” stalwarts like the Washington Post (4/2/15) and Politico (8/11/15) warned us that war with Iran was the most likely alternative to the JCPOA, conservative standard-bearers such as Fox News (7/14/15) and the Washington Times (8/10/15) foretold that war with Iran was the agreement’s most likely outcome. Three years hence, this dynamic has not changed.

+
Cartoonist Patrick Chappatte (New York Times, 5/10/18) presents Trump and Bolton’s “deal” for Iran.
+

To experience the full menu of US media’s single-mindedness about Iran, one need only buy a subscription to the New York Times. After Trump withdrew from the JCPOA, the Times’ editorial board (5/8/18) wrote that his move would “lay conditions for a possible wider war in the Middle East.” Susan Rice (New York Times5/8/18), President Barack Obama’s national security advisor, agreed: “We could face the choice of going to war or acquiescing to a nuclear-armed Iran,” she warned. Cartoonist Patrick Chappatte (New York Times5/10/18) was characteristically more direct, penning an image of Trump alongside Bolton, holding a fictitious new agreement featuring the singular, ultimate word: “WAR.”

+

On the other hand, calling Trump’s turn against JCPOA a “courageous decision,” Times columnist Bret Stephens (5/8/18) explained that the move was meant to force the Iranian government to make a choice: Either accede to US demands or “pursue their nuclear ambitions at the cost of economic ruin and possible war.” (Hardly courageous, when we all know there is no chance that Trump or Stephens would enlist should war materialize.)

+

Trump’s latest antics at the United Nations have spurred a wave of similar reaction across corporate media. Describing his threat to “totally destroy North Korea” at the UN General Assembly last year as “pointed and sharp,” Fox News anchor Eric Shawn (9/23/18) asked Bill Richardson, an Obama ally and President Bill Clinton’s ambassador to the UN, whether Trump would take the same approach toward Iran. “That aggressive policy we have with Iran is going to continue,” Richardson reassured the audience, “and I don’t think Iran is helping themselves.” In other words, if the United States starts a war with Iran, it’s totally Iran’s fault.

+

Politico (9/23/18), meanwhile, reported that Trump “is risking a potential war with Iran unless he engages the Islamist-led country using diplomacy.” In other words, if the United States starts a war with Iran, it’s totally Trump’s fault. Rice (New York Times9/26/18) reiterated her view that Trump’s rhetoric “presages the prospect of war in the Persian Gulf.” Whoever would be the responsible party is up for debate, but that war is in our future is apparently all but certain.

+

Politico’s article cited a statement signed by such esteemed US experts on war-making as Madeleine Albright, who presided over Clinton’s inhuman sanctions against Iraq in the ’90s, and Ryan Crocker, former ambassador for presidents George W. Bush and Obama to some of America’s favorite killing fields: Iraq, Afghanistan, Pakistan and Syria.  James Clapper, Obama’s National Intelligence Director, who also signed the letter, played an important role in trumping up WMD evidence against Saddam Hussein before the United States invaded Iraq in 2003. When it comes to US aggression, they’re the experts.

+

Vanity Fair (9/26/18) interviewed John Glaser of the Cato Institute, who called Trump’s strategy “pathetic,” and also warned that it forebodes war. In an effort to “one-up Obama,” Glaser explained, Trump’s plan is “to apply extreme economic pressure and explicit threats of war in order to get Iran to capitulate.” Sound familiar? As Glaser implies, this was exactly Obama’s strategy, only then it wasn’t seen as “pathetic,” but rather reasonable, and the sole means for preventing the war that every US pundit and politician saw around the corner (The Hill8/9/15).

+

When everyone decides that war is the only other possibility, it starts to look like an inevitability. But even when they aren’t overtly stoking war fever against Iran, corporate media prime the militaristic pump in more subtle yet equally disturbing ways.

+
Benjamin Netanyahu speaks for the Iranian people on CNN (9/29/18).
+

First among these is the near-complete erasure of Iranian voices from US airwaves (FAIR.org7/24/15). Rather than ask Iranians directly, national outlets like CNN (9/29/18) prefer to invite the prime minister of Israel, serial Iran alarmist and regional pariah Benjamin Netanyahu, to speak for them. During a jovial discussion this weekend over whether regime change and/or economic collapse is Iran’s most likely fate, Netanyahu explained to the audience that, either way, “The ones who will be happiest if that happens are the people of Iran.” No people of Iran were on hand to confirm or deny this assessment.

+

Bloomberg (9/30/18) similarly wanted to know, “What’s not to like about Trump’s Iran oil sanctions?” Julian Lee gleefully reported that “they are crippling exports from the Islamic Republic, at minimal cost to the US.” One might think the toll sanctions take on innocent Iranians would be something not to like, but Bloomberg merely worried that, notwithstanding the windfall for US refineries, “oil at $100 a barrel would be bad news for drivers everywhere—including those in the US.”

+

Another prized tactic is to whitewash Saudi Arabia, Iran’s chief geopolitical rival, whose genocidal destruction of Yemen is made possible by the United States, about which corporate media remain overwhelmingly silent (FAIR.org, 7/23/18). Iran’s involvement in Yemen, which both Trump and the New York Times (9/12/18) describe as “malign behavior,” is a principal justification for US support of Saudi Arabia, including the US-supplied bombs that recently ended the brief lives of over 40 Yemeni schoolchildren. Lockheed Martin’s stock is up 34 percentfrom Trump’s inauguration day.

+

Corporate media go beyond a simple coverup of Saudi crimes to evangelize their leadership as the liberal antidote to Iran’s “theocracy.” Who can forget Thomas Friedman’s revolting puff piece for the Saudi crown prince Mohammad bin Salman? Extensively quoting Salman (New York Times11/23/17), who refers to Iranian Ayatollah Ali Khamenei as “the new Hitler of the Middle East,” Friedman nevertheless remains pessimistic about whether “MBS and his team” can see their stand against Iran through, as “dysfunction and rivalries within the Sunni Arab world generally have prevented forming a unified front.” Oh well, every team needs cheerleaders, and Friedman isn’t just a fair-weather fan.

+

While Friedman (New York Times5/15/18) believes that Trump has drawn “some needed attention to Iran’s bad behavior,” for him pivotal questions remain unanswered, such as “who is going to take over in Tehran if the current Islamic regime collapses?” One immediate fix he proposed was to censure Iran’s metaphorical “occupation” of Syria, Iraq and Lebanon. Isn’t this ironic coming from an unapologetic propagandist for Washington’s decades-long, non-metaphorical occupation of the two countries to the east and west of Iran (FAIR.org12/9/15)?

+

In a surprising break from corporate media convention, USA Today (9/26/18) published a column on US/Iran relations written by an actual Iranian. Reflecting on the CIA-orchestrated coup against Iran’s elected government in 1953, Azadeh Shahshahani, who was born four days after the 1979 revolution there, wrote:

+
+

“I often wonder what would have happened if that coup had not worked, if [Prime Minister] Mosaddeq had been allowed to govern, if democracy had been allowed to flourish.”

+
+

“It is time for the US government to stop intervening in Iran and let the Iranian people determine their own destiny,” she beseeched readers.

+
Code Pink’s Medea Benjamin confronts the head of Trump’s “Iran Action Group” (Real News, 9/21/18).
+

Shahshahani’s call is supported by some who have rejected corporate media’s war propaganda and have gone to extreme lengths to have their perspectives heard. Anti-war activist and Code Pink  founder Medea Benjamin was recently forcibly removed after she upstaged Brian Hook, leader of Trump’s Iran Action Group, on live TV, calling his press conference “the most ridiculous thing I have ever seen” (Real News9/21/18). Benjamin implored the audience: “Let’s talk about Saudi Arabia. Is that who our allies are?”

+

“How dare you bring up the issue of Yemen,” admonished Benjamin as she was dragged from the room. “It’s the Saudi bombing that is killing most people in Yemen. So let’s get real. No more war! Peace with Iran!” Code Pink is currently petitioning the New York Times and Washington Post to stop propagandizing war.

+

Sadly, no matter whom you ask in corporate media, be they spokespeople for “Trump’s America” or “the resistance,” peace remains an elusive choice in the US political imagination. And while the public was focused last week on Supreme Court nominee Brett Kavanaugh’s perjurious testimony, the Senate finalized a$674 billion “defense” budget. Every single Democrat in the chamber voted in favor of the bill, explicitly naming Iran as persona non grata in the United States’world-leading arms supply network, which has seen a 25 percent increase in exports since Obama took office in 2009.

+

The US government’s imperial ambitions are perhaps its only truly bipartisan project—what the New York Times euphemistically refers to as “globalism.” Nowhere was this on fuller display than at the funeral for Republican Sen. John McCain (FAIR.org, 9/11/18), where politicians of all stripes were tripping over themselves to produce the best accolades for a man who infamously sang“bomb bomb bomb, bomb bomb Iran” to the tune of a Beach Boys song.

+

McCain’s bloodlust was nothing new. Nearly a hundred years ago, after the West’s imperial competition culminated in the most destructive war the world had ever seen, the brilliant American sociologist and anti-colonial author WEB Du Bois wrote, “This is not Europe gone mad; this is not aberration nor insanity; this is Europe.”

+

Iranian leaders have repeatedly said they do not want war with the US (AP9/27/18), but US corporate media, despite frequently characterizing Trump as a “mad king” (FAIR.org6/13/18), continue to play an instrumental role in rationalizing a future war with Iran. Should such an intentional catastrophe come to pass, we can hardly say that this would be America gone mad; war is not aberration, it is always presented as the next sane choice. This is America.

+

By John C. O’Day / Republished with permission / FAIR.org / Report a typo

+

This article was chosen for republication based on the interest of our readers. Anti-Media republishes stories from a number of other independent news sources. The views expressed in this article are the author’s own and do not reflect Anti-Media editorial policy.

\ No newline at end of file diff --git a/tests/convert_expected.md b/tests/convert_expected.md index e69de29..f81bb84 100644 --- a/tests/convert_expected.md +++ b/tests/convert_expected.md @@ -0,0 +1,56 @@ +Markdown Test! +============== + +**([FAIR.org](https://fair.org/home/trump-admin-follows-corporate-media-playbook-for-war-with-iran/))** — Three years ago, as Americans debated the Joint Comprehensive Plan of Action (JCPOA) agreement with the Islamic Republic of Iran—popularly known as “the Iran deal”—I highlighted a troubling media trend on **FAIR.org** ([8/20/15](https://fair.org/home/giving-war-a-chance/)): “For nearly all commentators, regardless of their position, war is the only alternative to that position.” + +In the months since US President Donald Trump tore up the JCPOA agreement, his administration has been trying to make good on corporate media’s collective prediction. Last week, John Bolton (**BBC**, [9/26/18](https://www.bbc.com/news/world-us-canada-45647863)), Trump’s national security advisor and chief warmonger, told Iran’s leaders and the world that there would be “hell to pay” if they dare to “cross us.” + +![](https://theantimedia.com/wp-content/uploads/2018/10/BBC-John-Bolton-610x343.jpg)John Bolton (**BBC**, [9/26/18](https://www.bbc.com/news/world-us-canada-45647863)): “Let my message today be clear: We are watching, and we will come after you.”That Bolton’s bellicose statements do not send shockwaves of pure horror across a [debt-strapped](https://www.reuters.com/article/us-usa-economy-budget/u-s-government-posts-214-billion-deficit-in-august-idUSKCN1LT2XL) and [war-weary](https://nyti.ms/2N7cg3m) United States is thanks in large part to incessant priming for war, facilitated by corporate media across the entire political spectrum, with a particular focus on Iran. + +Back in 2015, while current “resistance” stalwarts like the **Washington Post** ([4/2/15](https://www.washingtonpost.com/opinions/a-nuclear-deal-with-iran-is-the-best-option/2015/04/02/bc8292d2-d978-11e4-8103-fa84725dbf9d_story.html)) and **Politico** ([8/11/15](//www.politico.com/magazine/story/2015/08/iran-deal-rejection-121257.html)) warned us that war with Iran was the most likely alternative to the JCPOA, conservative standard-bearers such as **Fox News** ([7/14/15](//insider.foxnews.com/2015/07/14/dick-cheney-hannity-iran-nuke-deal-brings-world-closer-nuclear-war)) and the **Washington Times** ([8/10/15](//www.washingtontimes.com/news/2015/aug/10/ed-feulner-iran-nuclear-deal-makes-war-more-likely/)) foretold that war with Iran was the agreement’s most likely outcome. Three years hence, this dynamic has not changed. + +![](https://theantimedia.com/wp-content/uploads/2018/10/NYT-Iran-Deal-War-610x411.jpg)Cartoonist Patrick Chappatte (**New York Times**, [5/10/18](https://nyti.ms/2I9LCnJ)) presents Trump and Bolton’s “deal” for Iran.To experience the full menu of US media’s single-mindedness about Iran, one need only buy a subscription to the **New York Times**. After Trump withdrew from the JCPOA, the **Times**’ editorial board ([5/8/18](https://nyti.ms/2HXldJD)) wrote that his move would “lay conditions for a possible wider war in the Middle East.” Susan Rice (**New York Times**, [5/8/18](https://nyti.ms/2FXt4kH)), President Barack Obama’s national security advisor, agreed: “We could face the choice of going to war or acquiescing to a nuclear-armed Iran,” she warned. Cartoonist Patrick Chappatte (**New York Times**, [5/10/18](https://nyti.ms/2I9LCnJ)) was characteristically more direct, penning an image of Trump alongside Bolton, holding a fictitious new agreement featuring the singular, ultimate word: “WAR.” + +On the other hand, calling Trump’s turn against JCPOA a “courageous decision,” **Times** columnist Bret Stephens ([5/8/18](https://nyti.ms/2FWTz9O)) explained that the move was meant to force the Iranian government to make a choice: Either accede to US demands or “pursue their nuclear ambitions at the cost of economic ruin and possible war.” (Hardly courageous, when we all know there is no chance that Trump or Stephens would enlist should war materialize.) + +Trump’s [latest antics](https://youtu.be/KfVdIKaQzW8) at the United Nations have spurred a wave of similar reaction across corporate media. Describing his threat to “totally destroy North Korea” at the UN General Assembly last year as “pointed and sharp,” **Fox News** anchor Eric Shawn ([9/23/18](https://video.foxnews.com/v/5838934710001/)) asked Bill Richardson, an Obama ally and President Bill Clinton’s ambassador to the UN, whether Trump would take the same approach toward Iran. “That aggressive policy we have with Iran is going to continue,” Richardson reassured the audience, “and I don’t think Iran is helping themselves.” In other words, if the United States starts a war with Iran, it’s totally Iran’s fault. + +**Politico** ([9/23/18](https://www.politico.com/story/2018/09/23/trump-iran-war-foreign-policy-836411)), meanwhile, reported that Trump “is risking a potential war with Iran unless he engages the Islamist-led country using diplomacy.” In other words, if the United States starts a war with Iran, it’s totally Trump’s fault. Rice (**New York Times**, [9/26/18](https://nyti.ms/2N4AZjF)) reiterated her view that Trump’s rhetoric “presages the prospect of war in the Persian Gulf.” Whoever would be the responsible party is up for debate, but that war is in our future is apparently all but certain. + +**Politico**’s article cited a [statement](https://s3-us-west-1.amazonaws.com/coalitionagainstirannukes/Statement_Sept18.pdf) signed by such esteemed US experts on war-making as Madeleine Albright, who presided over Clinton’s [inhuman sanctions](https://nyti.ms/2tvy0MU) against Iraq in the ’90s, and Ryan Crocker, former ambassador for presidents George W. Bush and Obama to some of America’s favorite killing fields: Iraq, Afghanistan, Pakistan and Syria. James Clapper, Obama’s National Intelligence Director, who also signed the letter, played an [important role](https://www.washingtontimes.com/news/2010/jun/4/likely-intel-chief-clapper-held-disputed-wmd-view/) in trumping up WMD evidence against Saddam Hussein before the United States invaded Iraq in 2003. When it comes to US aggression, they’re the experts. + +**Vanity Fair** ([9/26/18](https://www.vanityfair.com/news/2018/09/john-bolton-iran-united-nations)) interviewed John Glaser of the Cato Institute, who called Trump’s strategy “pathetic,” and also warned that it forebodes war. In an effort to “one-up Obama,” Glaser explained, Trump’s plan is “to apply extreme economic pressure and explicit threats of war in order to get Iran to capitulate.” Sound familiar? As Glaser implies, this was [exactly Obama’s strategy](https://theintercept.com/2015/08/06/obama-summarizes-record/), only then it wasn’t seen as “pathetic,” but rather reasonable, and the sole means for preventing the war that every US pundit and politician saw around the corner (**The Hill**, [8/9/15](https://thehill.com/blogs/ballot-box/presidential-races/250683-sanders-war-the-alternative-if-iran-deal-fails)). + +When everyone decides that war is the only other possibility, it starts to look like an inevitability. But even when they aren’t overtly stoking war fever against Iran, corporate media prime the militaristic pump in more subtle yet equally disturbing ways. + +![](https://theantimedia.com/wp-content/uploads/2018/10/CNN-Netanyahu-610x343.jpg)Benjamin Netanyahu speaks for the Iranian people on **CNN** ([9/29/18](https://www.cnn.com/videos/world/2018/09/29/labott-netanyahu-iran-regime-intv-sot-vpx.cnn)).First among these is the near-complete erasure of Iranian voices from US airwaves (**FAIR.org**, [7/24/15](https://fair.org/home/side-by-side-coverage-of-cuba-and-iran-highlights-shift-in-us-media-villain-making/)). Rather than ask Iranians directly, national outlets like **CNN** ([9/29/18](https://www.cnn.com/videos/world/2018/09/29/labott-netanyahu-iran-regime-intv-sot-vpx.cnn)) prefer to invite the prime minister of Israel, [serial Iran alarmist](https://theintercept.com/2015/03/02/brief-history-netanyahu-crying-wolf-iranian-nuclear-bomb/) and regional pariah Benjamin Netanyahu, to speak for them. During a jovial discussion this weekend over whether regime change and/or economic collapse is Iran’s most likely fate, Netanyahu explained to the audience that, either way, “The ones who will be happiest if that happens are the people of Iran.” No people of Iran were on hand to confirm or deny this assessment. + +**Bloomberg** ([9/30/18](https://www.bloomberg.com/view/articles/2018-09-30/what-s-not-to-like-about-trump-iran-oil-sanctions-100-oil)) similarly wanted to know, “What’s not to like about Trump’s Iran oil sanctions?” Julian Lee gleefully reported that “they are crippling exports from the Islamic Republic, at minimal cost to the US.” One might think the [toll sanctions take](https://www.aljazeera.com/news/2018/08/iran-doctors-sanctions-endangering-patients-lives-180830064012071.html) on innocent Iranians would be something not to like, but **Bloomberg** merely worried that, notwithstanding the windfall for US refineries, “oil at $100 a barrel would be bad news for drivers everywhere—including those in the US.” + +Another prized tactic is to [whitewash Saudi Arabia](https://www.counterpunch.org/2018/03/05/the-1-5-billion-campaign-to-whitewash-genocide-in-yemen/), Iran’s chief geopolitical rival, whose genocidal destruction of Yemen is made possible by the United States, about which corporate media remain overwhelmingly silent (**FAIR.org**,[ 7/23/18](https://fair.org/home/action-alert-its-been-over-a-year-since-msnbc-has-mentioned-us-war-in-yemen/)). Iran’s involvement in Yemen, which both Trump and the **New York Times** ([9/12/18](https://nyti.ms/2MqieXJ)) describe as “malign behavior,” is a principal justification for US support of Saudi Arabia, including the[ US-supplied bombs](https://www.cnn.com/2018/08/17/middleeast/us-saudi-yemen-bus-strike-intl/index.html) that recently ended the brief lives of over 40 Yemeni schoolchildren. Lockheed Martin’s stock is [up 34 percent](https://www.marketwatch.com/investing/stock/LMT/historical?siteid=mktw&date=01%2F20%2F2017)from Trump’s inauguration day. + +Corporate media go beyond a simple coverup of Saudi crimes to evangelize their leadership as the liberal antidote to Iran’s “theocracy.” Who can forget Thomas Friedman’s revolting puff piece for the Saudi crown prince Mohammad bin Salman? Extensively quoting Salman (**New York Times**, [11/23/17](https://nyti.ms/2i0mwfg)), who refers to Iranian Ayatollah Ali Khamenei as “the new Hitler of the Middle East,” Friedman nevertheless remains pessimistic about whether “MBS and his team” can see their stand against Iran through, as “dysfunction and rivalries within the Sunni Arab world generally have prevented forming a unified front.” Oh well, every team needs cheerleaders, and Friedman isn’t just a fair-weather fan. + +While Friedman (**New York Times**, [5/15/18](https://nyti.ms/2GmBgLo)) believes that Trump has drawn “some needed attention to Iran’s bad behavior,” for him pivotal questions remain unanswered, such as “who is going to take over in Tehran if the current Islamic regime collapses?” One immediate fix he proposed was to censure Iran’s metaphorical “occupation” of Syria, Iraq and Lebanon. Isn’t this ironic coming from an unapologetic propagandist for Washington’s decades-long, non-metaphorical occupation of the two countries to the east and west of Iran (**FAIR.org**, [12/9/15](https://fair.org/home/friedman-goes-after-trump-hey-massive-bombing-was-my-idea/))? + +In a surprising break from corporate media convention, **USA Today** ([9/26/18](https://www.usatoday.com/story/opinion/voices/2018/09/26/iran-united-states-foreign-policy-iraq-war-column/1379872002/)) published a column on US/Iran relations written by an actual Iranian. Reflecting on the [CIA-orchestrated coup](https://en.wikipedia.org/wiki/1953_Iranian_coup_d%27%C3%A9tat) against Iran’s elected government in 1953, Azadeh Shahshahani, who was born four days after the 1979 revolution there, wrote: + +> “I often wonder what would have happened if that coup had not worked, if \[Prime Minister\] Mosaddeq had been allowed to govern, if democracy had been allowed to flourish.” + +“It is time for the US government to stop intervening in Iran and let the Iranian people determine their own destiny,” she beseeched readers. + +![](https://theantimedia.com/wp-content/uploads/2018/10/Real-News-Code-Pink-610x343.png)Code Pink’s Medea Benjamin confronts the head of Trump’s “Iran Action Group” (**Real News**, [9/21/18](https://youtu.be/peJGmeg6ZE8)).Shahshahani’s call is supported by some who have rejected corporate media’s war propaganda and have gone to extreme lengths to have their perspectives heard. Anti-war activist and Code Pink founder Medea Benjamin was recently forcibly removed after she upstaged Brian Hook, leader of Trump’s Iran Action Group, on live TV, calling his press conference “the most ridiculous thing I have ever seen” (**Real News**, [9/21/18](https://youtu.be/peJGmeg6ZE8)). Benjamin implored the audience: “Let’s talk about Saudi Arabia. Is that who our allies are?” + +“How dare you bring up the issue of Yemen,” admonished Benjamin as she was dragged from the room. “It’s the Saudi bombing that is killing most people in Yemen. So let’s get real. No more war! Peace with Iran!” Code Pink is [currently petitioning](https://www.codepink.org/dont_iraq_iran?utm_campaign=iran_national_sept25) the **New York Times** and **Washington Post** to stop propagandizing war. + +Sadly, no matter whom you ask in corporate media, be they spokespeople for “Trump’s America” or “the resistance,” peace remains an elusive choice in the US political imagination. And while the public was focused last week on Supreme Court nominee [Brett Kavanaugh’s ](https://youtu.be/7zVOkb3CdZ0)[perjurious testimony](https://www.currentaffairs.org/2018/09/how-we-know-kavanaugh-is-lying), the Senate finalized a[$674 billion “defense” budget](https://www.democracynow.org/2018/9/20/headlines/senate_passes_674_billion_military_spending_bill). Every single Democrat in the chamber voted in favor of the bill, [explicitly naming Iran](https://www.congress.gov/bill/115th-congress/house-bill/6157/text) as persona non grata in the United States’[world-leading arms supply network](https://www.sipri.org/news/press-release/2018/asia-and-middle-east-lead-rising-trend-arms-imports-us-exports-grow-significantly-says-sipri), which has seen a 25 percent increase in exports since Obama took office in 2009. + +The US government’s imperial ambitions are perhaps its only truly bipartisan project—what the **New York Times** euphemistically refers to as “globalism.” Nowhere was this on fuller display than at the funeral for Republican Sen. John McCain (**FAIR.org**,[ 9/11/18](https://fair.org/home/maverick-media-use-mccain-funeral-to-shore-up-us-imperialism/)), where politicians of all stripes were tripping over themselves to produce the best accolades for a man who [infamously sang](https://youtu.be/o-zoPgv_nYg)“bomb bomb bomb, bomb bomb Iran” to the tune of a Beach Boys song. + +McCain’s bloodlust was nothing new. Nearly a hundred years ago, after the West’s imperial competition culminated in the most destructive war the world had ever seen, the brilliant American sociologist and anti-colonial author WEB Du Bois [wrote](https://www.gutenberg.org/files/15210/15210-h/15210-h.htm), “This is not Europe gone mad; this is not aberration nor insanity; this *is* Europe.” + +Iranian leaders have repeatedly said they do not want war with the US (**AP**, [9/27/18](https://apnews.com/1cb23ee1641041c5910595715f2ea334)), but US corporate media, despite frequently characterizing Trump as a “mad king” (**FAIR.org**, [6/13/18](https://fair.org/home/why-do-us-media-only-worry-about-one-authoritarians-nukes/)), continue to play an instrumental role in rationalizing a future war with Iran. Should such an intentional catastrophe come to pass, we can hardly say that this would be America gone mad; war is not aberration, it is always presented as the next sane choice. This *is* America. + +*By [John C. O’Day](https://fair.org/author/john-c-oday/) / Republished with permission / [FAIR.org](https://fair.org/) / [Report a typo](mailto:edits@theantimedia.org)* + +*This article was chosen for republication based on the interest of our readers. Anti-Media republishes stories from a number of other independent news sources. The views expressed in this article are the author’s own and do not reflect Anti-Media editorial policy.* \ No newline at end of file diff --git a/tests/convert_initial.html b/tests/convert_initial.html deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test-lbry_network_parser.php b/tests/test-lbry_network_parser.php index 279b9c1..a398266 100644 --- a/tests/test-lbry_network_parser.php +++ b/tests/test-lbry_network_parser.php @@ -19,5 +19,20 @@ class LBRY_Network_Parser_Test extends WP_UnitTestCase public function test_convert_to_markdown() { + $content = file_get_contents(LBRY_ABSPATH . 'tests/convert_content.txt'); + $post = self::factory()->post->create_and_get(array( + 'post_title' => 'Markdown Test!', + 'post_content' => $content + )); + $attachment = $this->factory->attachment->create_and_get(array( + 'post_parent' => $post->ID, + 'file' => '/wp-content/uploads/2018/08/BBC-John-Bolton-610x343.jpg' + )); + add_post_meta($post->ID, '_thumbnail_id', $attachment->ID, true); + + $actual = fopen(LBRY_ABSPATH . 'tests/convert_actual.md', 'w'); + $converted = $this->class_instance->convert_to_markdown($post->ID); + fwrite($actual, $converted); + $this->assertFileEquals(LBRY_ABSPATH . 'tests/convert_expected.md', LBRY_ABSPATH . 'tests/convert_actual.md'); } } From 90de7cb6f6089363795cd4d91f88c26c6f5bda88 Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Thu, 11 Oct 2018 20:21:49 -0500 Subject: [PATCH 09/13] Added post widget --- classes/LBRY_Network.php | 56 ++++++++++++++++++++++++++++++++++++++++ classes/lbrypress.php | 8 +++++- templates/meta_box.php | 23 +++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 templates/meta_box.php diff --git a/classes/LBRY_Network.php b/classes/LBRY_Network.php index e571557..6caf40a 100644 --- a/classes/LBRY_Network.php +++ b/classes/LBRY_Network.php @@ -7,10 +7,66 @@ class LBRY_Network { + + /** + * The Publishing Object + * @var LBRY_Network_Publisher + */ + public $publisher = null; + + /** + * The Parsing Object + * @var LBRY_Network_Parser + */ + public $parser = null; + /** * [__construct description] */ public function __construct() { + $this->publisher = new LBRY_Network_Publisher(); + $this->parser = new LBRY_Network_Parser(); + + $this->post_meta_setup(); + } + + /** + * Sets up everything for the post meta boxes + */ + private function post_meta_setup() + { + // Add the meta boxes + add_action('add_meta_boxes', array($this, 'add_meta_boxes')); + + // Save the post meta on 'save_post' hook + add_action('save_post', array($this, 'save_post_meta')); + } + + /** + * Adds the meta boxes to the post editing backend + */ + public function add_meta_boxes() + { + add_meta_box( + 'lbry-network-publishing', // Unique ID + 'LBRY Network', // Title + array($this, 'meta_box_html'), // Callback function + 'post', // Screen Options (or post type) + 'side', // Context + 'high' // Priority + ); + } + + /** + * Handles saving the post meta that is relative to publishing to the LBRY Network + */ + public function save_post_meta() + { + } + + public function meta_box_html() + { + require_once(LBRY_ABSPATH . 'templates/meta_box.php'); } } diff --git a/classes/lbrypress.php b/classes/lbrypress.php index b087fae..9043ef1 100644 --- a/classes/lbrypress.php +++ b/classes/lbrypress.php @@ -38,6 +38,11 @@ class LBRYPress */ public $notice = null; + /** + * The Library Network Object + */ + public $network = null; + /** * Main LBRYPress Instance. * @@ -123,7 +128,6 @@ class LBRYPress { $this->daemon = new LBRY_Daemon(); $this->speech = new LBRY_Speech(); - $this->notice = new LBRY_Admin_Notice(); } /** @@ -137,6 +141,8 @@ class LBRYPress // Admin request if (is_admin()) { $this->admin = new LBRY_Admin(); + $this->notice = new LBRY_Admin_Notice(); + $this->network = new LBRY_Network(); } else { $this->speech->maybe_rewrite_urls(); } diff --git a/templates/meta_box.php b/templates/meta_box.php new file mode 100644 index 0000000..0971faa --- /dev/null +++ b/templates/meta_box.php @@ -0,0 +1,23 @@ + 'Unattributed', + 'permanent_url' => 'unattributed' +); +$channels = LBRY()->daemon->channel_list(); +array_unshift($channels, $unnatributed); +?> +

Choose which channels you would like to publish this post to:

+
    + + +
  • + +
    +
  • + + +
From 798ac5f30e18f1e5cc6594cc8b24078f8d3099ec Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Thu, 11 Oct 2018 22:59:01 -0500 Subject: [PATCH 10/13] Populate meta box with current selections --- classes/LBRY_Network.php | 31 +++++++++++++++++++++++++++---- templates/meta_box.php | 8 +++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/classes/LBRY_Network.php b/classes/LBRY_Network.php index 6caf40a..8a7bce8 100644 --- a/classes/LBRY_Network.php +++ b/classes/LBRY_Network.php @@ -40,7 +40,7 @@ class LBRY_Network add_action('add_meta_boxes', array($this, 'add_meta_boxes')); // Save the post meta on 'save_post' hook - add_action('save_post', array($this, 'save_post_meta')); + add_action('save_post', array($this, 'save_post_meta'), 10, 2); } /** @@ -54,18 +54,41 @@ class LBRY_Network array($this, 'meta_box_html'), // Callback function 'post', // Screen Options (or post type) 'side', // Context - 'high' // Priority + 'high' // Priority ); } /** * Handles saving the post meta that is relative to publishing to the LBRY Network */ - public function save_post_meta() + public function save_post_meta($post_id, $post) { + // Verify the nonce before proceeding. + if (!isset($_POST['_lbrynonce']) || !wp_verify_nonce($_POST['_lbrynonce'], 'lbry_publish_channels')) { + return $post_id; + } + + // Check if the current user has permission to edit the post. + $post_type = get_post_type_object($post->post_type); + if (!current_user_can($post_type->cap->edit_post, $post_id)) { + return $post_id; + } + + $meta_key = 'lbry_channels'; + $new_channels = (isset($_POST[$meta_key]) ? $_POST[$meta_key] : null); + $cur_channels = get_post_meta($post_id, $meta_key); + + // COMBAK: Make this a bit more efficent if they have lots of channels + // Start with clean meta, then add new channels if there are any + delete_post_meta($post_id, $meta_key); + if ($new_channels) { + foreach ($new_channels as $channel) { + add_post_meta($post_id, $meta_key, $channel); + } + } } - public function meta_box_html() + public function meta_box_html($post) { require_once(LBRY_ABSPATH . 'templates/meta_box.php'); } diff --git a/templates/meta_box.php b/templates/meta_box.php index 0971faa..93e73fa 100644 --- a/templates/meta_box.php +++ b/templates/meta_box.php @@ -6,14 +6,20 @@ $unnatributed = (object) array( ); $channels = LBRY()->daemon->channel_list(); array_unshift($channels, $unnatributed); +$cur_channels = get_post_meta($post->ID, 'lbry_channels'); ?> +

Choose which channels you would like to publish this post to:


  • From 31f47317c212ac58589919820f925384e7f36612 Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Fri, 12 Oct 2018 01:22:20 -0500 Subject: [PATCH 11/13] Made some notes for future edits --- classes/LBRY_Network.php | 3 +++ classes/LBRY_Network_Publisher.php | 12 ++++++++++++ templates/options_page.php | 6 +----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/classes/LBRY_Network.php b/classes/LBRY_Network.php index 8a7bce8..346059b 100644 --- a/classes/LBRY_Network.php +++ b/classes/LBRY_Network.php @@ -86,6 +86,9 @@ class LBRY_Network add_post_meta($post_id, $meta_key, $channel); } } + + // Publish the post on the LBRY Network + $this->publisher->publish($post, $new_channels); } public function meta_box_html($post) diff --git a/classes/LBRY_Network_Publisher.php b/classes/LBRY_Network_Publisher.php index 76b9370..0cd6235 100644 --- a/classes/LBRY_Network_Publisher.php +++ b/classes/LBRY_Network_Publisher.php @@ -13,4 +13,16 @@ class LBRY_Network_Publisher public function __construct() { } + + /** + * Publish the post to the LBRY Network + * @param int $post_id The ID of the post we are publishing + * @param array $channels An array of channels we are publishing to + */ + public function publish($post, $channels) + { + $name = $post->post_name; + + return; + } } diff --git a/templates/options_page.php b/templates/options_page.php index 5a2932f..320f2ea 100644 --- a/templates/options_page.php +++ b/templates/options_page.php @@ -2,14 +2,12 @@ $LBRY = LBRY(); $wallet_balance = $LBRY->daemon->wallet_balance(); $channel_list = $LBRY->daemon->channel_list(); +// TODO: Make this page look cleaner ?>
    -

    -

    Your wallet amount:

    -
    daemon->channel_list(); submit_button('Save Settings'); ?>
    -

    Your Publishable Channels

      @@ -28,7 +25,6 @@ $channel_list = $LBRY->daemon->channel_list();

      Looks like you haven't added any channels yet, feel free to do so below:

      -

      Add a new channel to publish to:

      From 8bc336952d1a425b914ddeeb3026e33765cf2643 Mon Sep 17 00:00:00 2001 From: Paul Kirby Date: Fri, 12 Oct 2018 02:42:40 -0500 Subject: [PATCH 12/13] Published post to lbry network --- classes/LBRY_Daemon.php | 29 ++++++++++++++++++++++ classes/LBRY_Network_Parser.php | 26 +++++++++++--------- classes/LBRY_Network_Publisher.php | 39 +++++++++++++++++++++++++++--- classes/lbrypress.php | 1 + templates/meta_box.php | 7 +++--- 5 files changed, 84 insertions(+), 18 deletions(-) diff --git a/classes/LBRY_Daemon.php b/classes/LBRY_Daemon.php index 13171ca..9f1c09d 100644 --- a/classes/LBRY_Daemon.php +++ b/classes/LBRY_Daemon.php @@ -80,6 +80,35 @@ class LBRY_Daemon return $result->result; } + /** + * Publishes a post to the LBRY Network + * @param [type] $name [description] + * @param [type] $bid [description] + * @param [type] $filepath [description] + * @param [type] $title [description] + * @param [type] $description [description] + * @param [type] $language [description] + * @return [type] [description] + */ + public function publish($name, $bid, $filepath, $title, $description, $language, $channel) + { + // TODO: Bring thumbnails into the mix + $result = $this->request( + 'publish', + array( + 'name' => $name, + 'bid' => $bid, + 'file_path' => $filepath, + 'title' => $title, + 'description' => $description, + 'language' => $language, + 'channel_name' => $channel + ) + ); + $this->check_for_errors($result); + return $result; + } + /** * Sends a cURL request to the LBRY Daemon * @param string $method The method to call on the LBRY API diff --git a/classes/LBRY_Network_Parser.php b/classes/LBRY_Network_Parser.php index 4ac3fad..6c7a0d7 100644 --- a/classes/LBRY_Network_Parser.php +++ b/classes/LBRY_Network_Parser.php @@ -22,18 +22,22 @@ class LBRY_Network_Parser )); } - public function convert_to_markdown($post_id) + /** + * Converts a post into markdown. + * @param WP_Post $post The post to be converted + * @return string + */ + public function convert_to_markdown($post) { - $post = get_post($post_id); - $title = '

      ' . $post->post_title . '

      '; - - $featured_image = get_the_post_thumbnail($post); - - $content = $title; - if ($featured_image) { - $content .= $featured_image . '
      '; - } - $content .= apply_filters('the_content', get_post($post_id)->post_content); + // $title = '

      ' . $post->post_title . '

      '; + // + // $featured_image = get_the_post_thumbnail($post); + // + // $content = $title; + // if ($featured_image) { + // $content .= $featured_image . '
      '; + // } + $content = apply_filters('the_content', $post->post_content); $converted = $this->converter->convert($content); return $converted; diff --git a/classes/LBRY_Network_Publisher.php b/classes/LBRY_Network_Publisher.php index 0cd6235..6d4cefa 100644 --- a/classes/LBRY_Network_Publisher.php +++ b/classes/LBRY_Network_Publisher.php @@ -21,8 +21,41 @@ class LBRY_Network_Publisher */ public function publish($post, $channels) { - $name = $post->post_name; - - return; + // Leave if nothing to publish to + if (!$channels) { + return; + } + + // Get converted markdown into a file + $filepath = LBRY_ABSPATH . 'tmp/' . $post->post_name . time() . '.md'; + $file = fopen($filepath, 'w'); + $converted = LBRY()->network->parser->convert_to_markdown($post); + $write_status = $file && fwrite($file, $converted); + fclose($file); + + // TODO: Catch relative exceptions if necessary + try { + // If everything went well with the conversion, carry on + if ($write_status) { + $featured_image = get_the_post_thumbnail($post); + + $name = $post->post_name; + $bid = get_option(LBRY_SETTINGS)[LBRY_LBC_PUBLISH]; + $title = $post->post_title; + $language = substr(get_locale(), 0, 2); + $license = get_option(LBRY_SETTINGS)[LBRY_LICENSE]; + // TODO: See if we can grab from yoast or a default? + $description = $post->post_title; + // TODO: Bring thumbnails into the mix + // $thumbnail = $featured_image ? $featured_image : null; + + foreach ($channels as $channel) { + LBRY()->daemon->publish($name, $bid, $filepath, $title, $description, $language, $channel); + } + } + } finally { + // Delete the temporary markdown file + unlink($filepath); + } } } diff --git a/classes/lbrypress.php b/classes/lbrypress.php index 9043ef1..721ef56 100644 --- a/classes/lbrypress.php +++ b/classes/lbrypress.php @@ -66,6 +66,7 @@ class LBRYPress spl_autoload_register(array($this, 'lbry_autoload_register')); $this->init(); $this->init_hooks(); + error_log("language: " . get_locale()); } /** diff --git a/templates/meta_box.php b/templates/meta_box.php index 93e73fa..35d0846 100644 --- a/templates/meta_box.php +++ b/templates/meta_box.php @@ -1,8 +1,7 @@ 'Unattributed', - 'permanent_url' => 'unattributed' + 'name' => 'unattributed', ); $channels = LBRY()->daemon->channel_list(); array_unshift($channels, $unnatributed); @@ -15,8 +14,8 @@ $cur_channels = get_post_meta($post->ID, 'lbry_channels');