From c741fba06c6d866c86306445d625a791249ad3fc Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Wed, 21 Jan 2015 10:50:53 +0100 Subject: Update lib_opml.php - lib_opml was not in its newest version - FRSS supports OPML file without text attributes Fix https://github.com/FreshRSS/FreshRSS/issues/758 --- lib/lib_opml.php | 128 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 26 deletions(-) (limited to 'lib/lib_opml.php') diff --git a/lib/lib_opml.php b/lib/lib_opml.php index f320335bb..02ae5f55c 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -1,11 +1,19 @@ + * @link https://github.com/marienfressinaud/lib_opml + * @version 0.2 + * @license public domain * * Usages: * > include('lib_opml.php'); @@ -23,21 +31,44 @@ * > echo $opml_string; * > print_r($opml_object); * + * You can set $strict argument to false if you want to bypass "text" attribute + * requirement. + * * If parsing fails for any reason (e.g. not an XML string, does not match with * the specifications), a LibOPML_Exception is raised. * - * Author: Marien Fressinaud - * Url: https://github.com/marienfressinaud/lib_opml - * Version: 0.1 - * Date: 2014-03-29 - * License: public domain + * lib_opml array format is described here: + * $array = array( + * 'head' => array( // 'head' element is optional (but recommended) + * 'key' => 'value', // key must be a part of available OPML head elements + * ), + * 'body' => array( // body is required + * array( // this array represents an outline (at least one) + * 'text' => 'value', // 'text' element is required if $strict is true + * 'key' => 'value', // key and value are what you want (optional) + * '@outlines' = array( // @outlines is a special value and represents sub-outlines + * array( + * [...] // where [...] is a valid outline definition + * ), + * ), + * ), + * array( // other outline definitions + * [...] + * ), + * [...], + * ) + * ) * - * */ + */ +/** + * A simple Exception class which represents any kind of OPML problem. + * Message should precise the current problem. + */ class LibOPML_Exception extends Exception {} -// These elements are optional +// Define the list of available head attributes. All of them are optional. define('HEAD_ELEMENTS', serialize(array( 'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail', 'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop', @@ -45,7 +76,16 @@ define('HEAD_ELEMENTS', serialize(array( ))); -function libopml_parse_outline($outline_xml) { +/** + * Parse an XML object as an outline object and return corresponding array + * + * @param SimpleXMLElement $outline_xml the XML object we want to parse + * @param bool $strict true if "text" attribute is required, false else + * @return array corresponding to an outline and following format described above + * @throws LibOPML_Exception + * @access private + */ +function libopml_parse_outline($outline_xml, $strict = true) { $outline = array(); // An outline may contain any kind of attributes but "text" attribute is @@ -59,7 +99,7 @@ function libopml_parse_outline($outline_xml) { } } - if (!$text_is_present) { + if (!$text_is_present && $strict) { throw new LibOPML_Exception( 'Outline does not contain any text attribute' ); @@ -68,7 +108,7 @@ function libopml_parse_outline($outline_xml) { foreach ($outline_xml->children() as $key => $value) { // An outline may contain any number of outline children if ($key === 'outline') { - $outline['@outlines'][] = libopml_parse_outline($value); + $outline['@outlines'][] = libopml_parse_outline($value, $strict); } else { throw new LibOPML_Exception( 'Body can contain only outline elements' @@ -80,7 +120,16 @@ function libopml_parse_outline($outline_xml) { } -function libopml_parse_string($xml) { +/** + * Parse a string as a XML one and returns the corresponding array + * + * @param string $xml is the string we want to parse + * @param bool $strict true if "text" attribute is required, false else + * @return array corresponding to the XML string and following format described above + * @throws LibOPML_Exception + * @access public + */ +function libopml_parse_string($xml, $strict = true) { $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; @@ -101,7 +150,6 @@ function libopml_parse_string($xml) { // First, we get all "head" elements. Head is required but its sub-elements // are optional. - // TODO: test head exists! foreach ($opml->head->children() as $key => $value) { if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { $array['head'][$key] = (string)$value; @@ -115,11 +163,10 @@ function libopml_parse_string($xml) { // Then, we get body oulines. Body must contain at least one outline // element. $at_least_one_outline = false; - // TODO: test body exists! foreach ($opml->body->children() as $key => $value) { if ($key === 'outline') { $at_least_one_outline = true; - $array['body'][] = libopml_parse_outline($value); + $array['body'][] = libopml_parse_outline($value, $strict); } else { throw new LibOPML_Exception( 'Body can contain only outline elements' @@ -137,7 +184,16 @@ function libopml_parse_string($xml) { } -function libopml_parse_file($filename) { +/** + * Parse a string contained into a file as a XML string and returns the corresponding array + * + * @param string $filename should indicates a valid XML file + * @param bool $strict true if "text" attribute is required, false else + * @return array corresponding to the file content and following format described above + * @throws LibOPML_Exception + * @access public + */ +function libopml_parse_file($filename, $strict = true) { $file_content = file_get_contents($filename); if ($file_content === false) { @@ -146,11 +202,20 @@ function libopml_parse_file($filename) { ); } - return libopml_parse_string($file_content); + return libopml_parse_string($file_content, $strict); } -function libopml_render_outline($parent_elt, $outline) { +/** + * Create a XML outline object in a parent object. + * + * @param SimpleXMLElement $parent_elt is the parent object of current outline + * @param array $outline array representing an outline object + * @param bool $strict true if "text" attribute is required, false else + * @throws LibOPML_Exception + * @access private + */ +function libopml_render_outline($parent_elt, $outline, $strict) { // Outline MUST be an array! if (!is_array($outline)) { throw new LibOPML_Exception( @@ -165,7 +230,7 @@ function libopml_render_outline($parent_elt, $outline) { // outline elements. if ($key === '@outlines' && is_array($value)) { foreach ($value as $outline_child) { - libopml_render_outline($outline_elt, $outline_child); + libopml_render_outline($outline_elt, $outline_child, $strict); } } elseif (is_array($value)) { throw new LibOPML_Exception( @@ -181,7 +246,7 @@ function libopml_render_outline($parent_elt, $outline) { } } - if (!$text_is_present) { + if (!$text_is_present && $strict) { throw new LibOPML_Exception( 'You must define at least a text element for all outlines' ); @@ -189,8 +254,19 @@ function libopml_render_outline($parent_elt, $outline) { } -function libopml_render($array, $as_xml_object = false) { - $opml = new SimpleXMLElement(''); +/** + * Render an array as an OPML string or a XML object. + * + * @param array $array is the array we want to render and must follow structure defined above + * @param bool $as_xml_object false if function must return a string, true for a XML object + * @param bool $strict true if "text" attribute is required, false else + * @return string|SimpleXMLElement XML string corresponding to $array or XML object + * @throws LibOPML_Exception + * @access public + */ +function libopml_render($array, $as_xml_object = false, $strict = true) { + $opml = new SimpleXMLElement(''); + $opml->addAttribute('version', $strict ? '2.0' : '1.0'); // Create head element. $array['head'] is optional but head element will // exist in the final XML object. @@ -218,7 +294,7 @@ function libopml_render($array, $as_xml_object = false) { // Create outline elements $body = $opml->addChild('body'); foreach ($array['body'] as $outline) { - libopml_render_outline($body, $outline); + libopml_render_outline($body, $outline, $strict); } // And return the final result -- cgit v1.2.3 From 9d1f35d4b859a44964e52a377e8718113e160862 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 6 Mar 2016 17:04:21 +0100 Subject: OPML bug import not using title Fix https://github.com/FreshRSS/FreshRSS/issues/1048 --- lib/lib_opml.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/lib_opml.php') diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 02ae5f55c..66b854313 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -105,6 +105,10 @@ function libopml_parse_outline($outline_xml, $strict = true) { ); } + if (empty($outline['text']) && isset($outline['title'])) { + $outline['text'] = $outline['title']; + } + foreach ($outline_xml->children() as $key => $value) { // An outline may contain any number of outline children if ($key === 'outline') { -- cgit v1.2.3 From 29d79185880f361545cf08b9faa4c755206c296e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 11 Aug 2016 22:44:42 +0200 Subject: Support for OPML 2.0 category attribute https://github.com/FreshRSS/FreshRSS/issues/1202 --- CHANGELOG.md | 2 ++ README.fr.md | 1 + README.md | 1 + lib/lib_opml.php | 31 ++++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) (limited to 'lib/lib_opml.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 601bc9d4b..017bd6e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,14 @@ * Fixed scroll in log view [#1178](https://github.com/FreshRSS/FreshRSS/issues/1178) * Fixed JavaScript bug when articles were not always marked as read [#1123](https://github.com/FreshRSS/FreshRSS/issues/1123) * Fixed Apache Etag issue that prevented caching [#1199](https://github.com/FreshRSS/FreshRSS/pull/1199) + * Fixed OPML import of categories [#1202](https://github.com/FreshRSS/FreshRSS/issues/1202) * UI * Updated to jQuery 3.1.0 and several JavaScript fixes (e.g. drag & drop) [#1197](https://github.com/FreshRSS/FreshRSS/pull/1197) * API * Add API link in FreshRSS profile settings to ease set-up [#1186](https://github.com/FreshRSS/FreshRSS/pull/1186) * Mics. * JSHint of JavaScript code and better initialisation [#1196](https://github.com/FreshRSS/FreshRSS/pull/1196) + * Updated credits, and images in README [#1201](https://github.com/FreshRSS/FreshRSS/issues/1201) ## 2016-07-23 FreshRSS 1.4.0 diff --git a/README.fr.md b/README.fr.md index a53653286..8324b9657 100644 --- a/README.fr.md +++ b/README.fr.md @@ -125,6 +125,7 @@ mysqldump -u utilisateur -p --databases freshrss > freshrss.sql * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/) * [jQuery](http://jquery.com/) * [ArthurHoaro/favicon](https://github.com/ArthurHoaro/favicon) +* [lib_opml](https://github.com/marienfressinaud/lib_opml) * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/) * [flotr2](http://www.humblesoftware.com/flotr2) diff --git a/README.md b/README.md index 1c9f89c78..844967f4f 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ mysqldump -u user -p --databases freshrss > freshrss.sql * [php-http-304](http://alexandre.alapetite.fr/doc-alex/php-http-304/) * [jQuery](http://jquery.com/) * [ArthurHoaro/favicon](https://github.com/ArthurHoaro/favicon) +* [lib_opml](https://github.com/marienfressinaud/lib_opml) * [keyboard_shortcuts](http://www.openjs.com/scripts/events/keyboard_shortcuts/) * [flotr2](http://www.humblesoftware.com/flotr2) diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 66b854313..0414868fb 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -12,7 +12,7 @@ * * @author Marien Fressinaud * @link https://github.com/marienfressinaud/lib_opml - * @version 0.2 + * @version 0.2-FreshRSS~1.5.1 * @license public domain * * Usages: @@ -123,6 +123,32 @@ function libopml_parse_outline($outline_xml, $strict = true) { return $outline; } +/** + * Reformat the XML document as a hierarchy when + * the OPML 2.0 category attribute is used + */ +function preprocessing_categories($doc) { + $outline_categories = []; + $body = $doc->getElementsByTagName('body')->item(0); + $xpath = new DOMXpath($doc); + $outlines = $xpath->query("/opml/body/outline[@category]"); + foreach ($outlines as $outline) { + $category = trim($outline->getAttribute('category')); + if ($category != '') { + $outline_categorie = null; + if (!isset($outline_categories[$category])) { + $outline_categorie = $doc->createElement('outline'); + $outline_categorie->setAttribute('text', $category); + $body->insertBefore($outline_categorie, $body->firstChild); + $outline_categories[$category] = $outline_categorie; + } else { + $outline_categorie = $outline_categories[$category]; + } + $outline->parentNode->removeChild($outline); + $outline_categorie->appendChild($outline); + } + } +} /** * Parse a string as a XML one and returns the corresponding array @@ -140,6 +166,9 @@ function libopml_parse_string($xml, $strict = true) { $dom->loadXML($xml); $dom->encoding = 'UTF-8'; + //Partial compatibility with the category attribute of OPML 2.0 + preprocessing_categories($dom); + $opml = simplexml_import_dom($dom); if (!$opml) { -- cgit v1.2.3 From 2c92860310d389f33fe7a10011e2a3921883b7bc Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 11 Aug 2016 23:13:28 +0200 Subject: Minor quotes --- lib/lib_opml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/lib_opml.php') diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 0414868fb..a3af54596 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -131,7 +131,7 @@ function preprocessing_categories($doc) { $outline_categories = []; $body = $doc->getElementsByTagName('body')->item(0); $xpath = new DOMXpath($doc); - $outlines = $xpath->query("/opml/body/outline[@category]"); + $outlines = $xpath->query('/opml/body/outline[@category]'); foreach ($outlines as $outline) { $category = trim($outline->getAttribute('category')); if ($category != '') { -- cgit v1.2.3 From 40f1873de790b28890d65263ec1f8426121ae951 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 12 Aug 2016 18:43:32 +0200 Subject: OPML compatibility PHP 5.3 https://github.com/FreshRSS/FreshRSS/issues/1202 https://github.com/FreshRSS/FreshRSS/pull/1206 --- lib/lib_opml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/lib_opml.php') diff --git a/lib/lib_opml.php b/lib/lib_opml.php index a3af54596..b89e92977 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -128,7 +128,7 @@ function libopml_parse_outline($outline_xml, $strict = true) { * the OPML 2.0 category attribute is used */ function preprocessing_categories($doc) { - $outline_categories = []; + $outline_categories = array(); $body = $doc->getElementsByTagName('body')->item(0); $xpath = new DOMXpath($doc); $outlines = $xpath->query('/opml/body/outline[@category]'); -- cgit v1.2.3