[
'name' => 'Source',
'type' => 'text',
'required' => true,
'title' => 'Insert wiki page URI (e.g.: https://moinmo.in/MoinMoin)',
'exampleValue' => 'https://moinmo.in/MoinMoin'
],
'separator' => [
'name' => 'Separator',
'type' => 'list',
'requied' => true,
'title' => 'Defines the separtor for splitting content into feeds',
'defaultValue' => 'h2',
'values' => [
'Header (h1)' => 'h1',
'Header (h2)' => 'h2',
'Header (h3)' => 'h3',
'List element (li)' => 'li',
'Anchor (a)' => 'a'
]
],
'limit' => [
'name' => 'Limit',
'type' => 'number',
'required' => false,
'title' => 'Number of items to return (from top)',
'defaultValue' => -1
],
'content' => [
'name' => 'Content',
'type' => 'list',
'required' => false,
'title' => 'Defines how feed contents are build',
'defaultValue' => 'separator',
'values' => [
'By separator' => 'separator',
'Follow link (only for anchor)' => 'follow',
'None' => 'none'
]
]
]
];
private $title = '';
public function collectData()
{
/* MoinMoin uses a rather unpleasent representation of HTML. Instead of
* using tags like ,
MoinMoin is a Wiki software implemented in * Python * and distributed as Free Software under * GNU GPL license. * ... */ $html = getSimpleHTMLDOM($this->getInput('source')); // Some anchors link to local sites or local IDs (both don't work well // in feeds) $html = $this->fixAnchors($html); $this->title = $html->find('title', 0)->innertext . ' | ' . self::NAME; // Here we focus on simple author and timestamp information from the given // page. Later we update this information in case the anchor is followed. $author = $this->findAuthor($html); $timestamp = $this->findTimestamp($html); $sections = $this->splitSections($html); foreach ($sections as $section) { $item = []; $item['uri'] = $this->findSectionAnchor($section[0]); switch ($this->getInput('content')) { case 'none': // Do not return any content break; case 'follow': // Follow the anchor // We can only follow anchors (use default otherwise) if ($this->getInput('separator') === 'a') { $content = $this->followAnchor($item['uri']); // Return only actual content $item['content'] = $content->find('div#page', 0)->innertext; // Each page could have its own author and timestamp $author = $this->findAuthor($content); $timestamp = $this->findTimestamp($content); break; } // fall-through case 'separator': default: // Use contents from the current page $item['content'] = $this->cleanArticle($section[2]); } if (!is_null($author)) { $item['author'] = $author; } if (!is_null($timestamp)) { $item['timestamp'] = $timestamp; } $item['title'] = strip_tags($section[1]); // Skip items with empty title if (empty(trim($item['title']))) { continue; } $this->items[] = $item; if ( $this->getInput('limit') > 0 && count($this->items) >= $this->getInput('limit') ) { break; } } } public function getName() { return $this->title ?: parent::getName(); } public function getURI() { return $this->getInput('source') ?: parent::getURI(); } /** * Splits the html into sections. * * Returns an array with one element per section. Each element consists of: * [0] The entire section * [1] The section title * [2] The section content */ private function splitSections($html) { $content = $html->find('div#page', 0)->innertext or returnServerError('Unable to find
!'); $sections = []; $regex = implode( '', [ "\<{$this->getInput('separator')}.+?(?=\>)\>", "(.+?)(?=\<\/{$this->getInput('separator')}\>)", "\<\/{$this->getInput('separator')}\>", "(.+?)((?=\<{$this->getInput('separator')})|(?=\MoinMoin: LocalSpellingWords * (last edited 2017-02-16 15:36:31 by hosted-by)
*/ $pageinfo = $html->find('[id="pageinfo"]', 0); if (is_null($pageinfo)) { return null; } else { $author = $pageinfo->find('[title=]', 0); if (is_null($author)) { return null; } else { return trim(explode('@', $author->title)[0]); } } } /** * Returns the time of last edit * * Notice: Some pages don't provide this information */ private function findTimestamp($html) { // See example of findAuthor() $pageinfo = $html->find('[id="pageinfo"]', 0); if (is_null($pageinfo)) { return null; } else { $timestamp = $pageinfo->innertext; $matches = []; preg_match('/.+?(?=\().+?(?=\d)([0-9\-\s\:]+)/m', $pageinfo, $matches); return strtotime($matches[1]); } } /** * Returns the original HTML with all anchors fixed (makes relative anchors * absolute) */ private function fixAnchors($html, $source = null) { $source = $source ?: $this->getURI(); foreach ($html->find('a') as $anchor) { switch (substr($anchor->href, 0, 1)) { case 'h': // http or https, no actions required break; case '/': // some relative path $anchor->href = $this->findDomain($source) . $anchor->href; break; case '#': // it's an ID default: // probably something like ? or &, skip empty ones if (!isset($anchor->href)) { break; } $anchor->href = $source . $anchor->href; } } return $html; } /** * Loads the full article of a given anchor (if the anchor is from the same * wiki domain) */ private function followAnchor($anchor) { if (strrpos($anchor, $this->findDomain($this->getInput('source')) === false)) { return null; } $html = getSimpleHTMLDOMCached($anchor); if (!$html) { // Cannot load article return null; } return $this->fixAnchors($html, $anchor); } /** * Finds the domain for a given URI */ private function findDomain($uri) { $matches = []; preg_match('/(http[s]{0,1}:\/\/.+?(?=\/))/', $uri, $matches); return $matches[1]; } /* This function is a copy from CNETBridge */ private function stripWithDelimiters($string, $start, $end) { while (strpos($string, $start) !== false) { $section_to_remove = substr($string, strpos($string, $start)); $section_to_remove = substr($section_to_remove, 0, strpos($section_to_remove, $end) + strlen($end)); $string = str_replace($section_to_remove, '', $string); } return $string; } /* This function is based on CNETBridge */ private function cleanArticle($article_html) { $article_html = $this->stripWithDelimiters($article_html, '