aboutsummaryrefslogtreecommitdiff
path: root/lib/lib_opml.php
diff options
context:
space:
mode:
authorGravatar berumuron <dev@marienfressinaud.fr> 2023-01-18 10:12:21 +0100
committerGravatar GitHub <noreply@github.com> 2023-01-18 10:12:21 +0100
commitdaaa391e33c5d92e3dd91bb0b81ac420abed7097 (patch)
treea3263c26ac90fb3115627e156eba580826acfd4f /lib/lib_opml.php
parent216e39c3cc43061686981b96328796765d264d29 (diff)
tec: Update the lib_opml (#4403)
* fix: Fix undefined GLOB_BRACE on Alpine The manual states that: > Note: The GLOB_BRACE flag is not available on some non GNU systems, > like Solaris or Alpine Linux. This generated an error on Alpine. Reference: https://www.php.net/manual/function.glob.php * fix: List details of feeds for OPML exportation The details are necessary to export the XPath information, the CSS full content path and read actions filters. * Update LibOpml to 0.4.0 * Refactor OPML importation to be more robust First, it fixes two regressions introduced by the update of lib_opml: - title attribute is used when text attribute is missing; - the OPML category attribute is used as a fallback for feeds categories. In a related way, if also fixes a problem when a feed had both a parent category outline and a category attribute. Before, it only considered the attribute as its category, but now it considers the parent outline. Then, it counts category limit correctly by not increasing `$nb_categories` if the category already exists. * Exclude lib_opml from the CodeSniffer * Fix variable names when logging some errors * Fix catch of LibOpml Exception * Make sure to declare the category * Exclude lib_opml from PHPStan analyze * Disable markdownlint for lib_opml * Fix typos * Use auto-loading and allow updates via Composer * Fix broken links to lib_opml * Bring back the ability to import the OPML frss:opmlUrl attribute * Refactor the logs of OPML errors * Update lib_opml to the version 0.5.0 Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Diffstat (limited to 'lib/lib_opml.php')
-rw-r--r--lib/lib_opml.php353
1 files changed, 0 insertions, 353 deletions
diff --git a/lib/lib_opml.php b/lib/lib_opml.php
deleted file mode 100644
index f86d780b7..000000000
--- a/lib/lib_opml.php
+++ /dev/null
@@ -1,353 +0,0 @@
-<?php
-
-/**
- * lib_opml is a free library to manage OPML format in PHP.
- *
- * By default, it takes in consideration version 2.0 but can be compatible with
- * OPML 1.0. More information on http://dev.opml.org.
- * Difference is "text" attribute is optional in version 1.0. It is highly
- * recommended to use this attribute.
- *
- * lib_opml requires SimpleXML (php.net/simplexml) and DOMDocument (php.net/domdocument)
- *
- * @author Marien Fressinaud <dev@marienfressinaud.fr>
- * @link https://github.com/marienfressinaud/lib_opml
- * @version 0.2-FreshRSS~1.20.0
- * @license public domain
- *
- * Usages:
- * > include('lib_opml.php');
- * > $filename = 'my_opml_file.xml';
- * > $opml_array = libopml_parse_file($filename);
- * > print_r($opml_array);
- *
- * > $opml_string = [...];
- * > $opml_array = libopml_parse_string($opml_string);
- * > print_r($opml_array);
- *
- * > $opml_array = [...];
- * > $opml_string = libopml_render($opml_array);
- * > $opml_object = libopml_render($opml_array, true);
- * > 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.
- *
- * 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 {}
-
-
-// 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',
- 'windowLeft', 'windowBottom', 'windowRight'
-)));
-
-
-/**
- * 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
- // required !
- $text_is_present = false;
-
- $elem = dom_import_simplexml($outline_xml);
- /** @var DOMAttr $attr */
- foreach ($elem->attributes as $attr) {
- $key = $attr->localName;
-
- if ($attr->namespaceURI == '') {
- $outline[$key] = $attr->value;
- } else {
- $outline[$key] = [
- 'namespace' => $attr->namespaceURI,
- 'value' => $attr->value,
- ];
- }
-
- if ($key === 'text') {
- $text_is_present = true;
- }
- }
-
- if (!$text_is_present && $strict) {
- throw new LibOPML_Exception(
- 'Outline does not contain any text attribute'
- );
- }
-
- 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') {
- $outline['@outlines'][] = libopml_parse_outline($value, $strict);
- }
- }
-
- return $outline;
-}
-
-/**
- * Reformat the XML document as a hierarchy when
- * the OPML 2.0 category attribute is used
- */
-function preprocessing_categories($doc) {
- $outline_categories = array();
- $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_category = null;
- if (!isset($outline_categories[$category])) {
- $outline_category = $doc->createElement('outline');
- $outline_category->setAttribute('text', $category);
- $body->insertBefore($outline_category, $body->firstChild);
- $outline_categories[$category] = $outline_category;
- } else {
- $outline_category = $outline_categories[$category];
- }
- $outline->parentNode->removeChild($outline);
- $outline_category->appendChild($outline);
- }
- }
-}
-
-/**
- * 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 to perform some validation (e.g. require "text" attribute), false to relax
- * @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;
- $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) {
- throw new LibOPML_Exception();
- }
-
- $array = array(
- 'version' => (string)$opml['version'],
- 'head' => array(),
- 'body' => array()
- );
-
- if (isset($opml->head)) {
- // We get all "head" elements. Head is required but its sub-elements are optional.
- foreach ($opml->head->children() as $key => $value) {
- if (in_array($key, unserialize(HEAD_ELEMENTS), true)) {
- $array['head'][$key] = (string)$value;
- } elseif ($strict) {
- throw new LibOPML_Exception($key . ' is not part of the OPML 2.0 specification');
- }
- }
- } elseif ($strict) {
- throw new LibOPML_Exception('Required OPML head element is missing!');
- }
-
- // Then, we get body oulines. Body must contain at least one outline
- // element.
- $at_least_one_outline = false;
- foreach ($opml->body->children() as $key => $value) {
- if ($key === 'outline') {
- $at_least_one_outline = true;
- $array['body'][] = libopml_parse_outline($value, $strict);
- }
- }
-
- if (!$at_least_one_outline) {
- throw new LibOPML_Exception(
- 'OPML body must contain at least one outline element'
- );
- }
-
- return $array;
-}
-
-
-/**
- * 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) {
- throw new LibOPML_Exception(
- $filename . ' cannot be found'
- );
- }
-
- return libopml_parse_string($file_content, $strict);
-}
-
-
-/**
- * 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(
- 'Outline element must be defined as array'
- );
- }
-
- $outline_elt = $parent_elt->addChild('outline');
- $text_is_present = false;
- /** @var string|array<string,mixed> $value */
- foreach ($outline as $key => $value) {
- // Only outlines can be an array and so we consider children are also
- // outline elements.
- if ($key === '@outlines' && is_array($value)) {
- foreach ($value as $outline_child) {
- libopml_render_outline($outline_elt, $outline_child, $strict);
- }
- } elseif (is_array($value) && !isset($value['namespace'])) {
- throw new LibOPML_Exception(
- 'Type of outline elements cannot be array (except for providing a namespace): ' . $key
- );
- } else {
- // Detect text attribute is present, that's good :)
- if ($key === 'text') {
- $text_is_present = true;
- }
- if (is_array($value)) {
- if (!empty($value['namespace']) && !empty($value['value'])) {
- $outline_elt->addAttribute($key, $value['value'], $value['namespace']);
- }
- } else {
- $outline_elt->addAttribute($key, $value);
- }
- }
- }
-
- if (!$text_is_present && $strict) {
- throw new LibOPML_Exception(
- 'You must define at least a text element for all outlines'
- );
- }
-}
-
-
-/**
- * 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></opml>');
- $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.
- $head = $opml->addChild('head');
- if (isset($array['head'])) {
- foreach ($array['head'] as $key => $value) {
- if (in_array($key, unserialize(HEAD_ELEMENTS), true)) {
- $head->addChild($key, $value);
- }
- }
- }
-
- // Check body is set and contains at least one element
- if (!isset($array['body'])) {
- throw new LibOPML_Exception(
- '$array must contain a body element'
- );
- }
- if (count($array['body']) <= 0) {
- throw new LibOPML_Exception(
- 'Body element must contain at least one element (array)'
- );
- }
-
- // Create outline elements
- $body = $opml->addChild('body');
- foreach ($array['body'] as $outline) {
- libopml_render_outline($body, $outline, $strict);
- }
-
- // And return the final result
- if ($as_xml_object) {
- return $opml;
- } else {
- $dom = dom_import_simplexml($opml)->ownerDocument;
- $dom->formatOutput = true;
- $dom->encoding = 'UTF-8';
- return $dom->saveXML();
- }
-}