From ce896b424775ad3c629b391bede1c63472e10bd6 Mon Sep 17 00:00:00 2001 From: logmanoriginal Date: Fri, 27 Jul 2018 23:07:25 +0200 Subject: [PATCH] [SkimfeedBridge] Add new bridge New bridge for Skimfeed: https://skimfeed.com Generates feeds for all features of Skimfeed: - News (the ones displayed on the front page) - Hot topics ("What's Hot" section on the front page) - Tech news (preconfigured feeds in the menu bar) - Custom feeds (using the configuration system of Skimfeed), see https://skimfeed.com/custom.php The number of items returned by the bridge can be limited for all categories ('&limit=...'). This parameter is optional, all categories are unlimited by default! Authors are added with HTML anchors in order to allow quick navigation to source channels. The bridge ships with developer tools to auto-generate lists in the future (especially useful for 'Tech news'!) References #748 --- bridges/SkimfeedBridge.php | 825 +++++++++++++++++++++++++++++++++++++ 1 file changed, 825 insertions(+) create mode 100644 bridges/SkimfeedBridge.php diff --git a/bridges/SkimfeedBridge.php b/bridges/SkimfeedBridge.php new file mode 100644 index 0000000..0b22b11 --- /dev/null +++ b/bridges/SkimfeedBridge.php @@ -0,0 +1,825 @@ + array( // auto-generated (see below) + 'box_channel' => array( + 'name' => 'Channel', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your channel', + 'values' => array( + 'Hacker News' => '/news/hacker-news.html', + 'QZ' => '/news/qz.html', + 'The Verge' => '/news/the-verge.html', + 'Slashdot' => '/news/slashdot.html', + 'Lifehacker' => '/news/lifehacker.html', + 'Gizmag' => '/news/gizmag.html', + 'Fast Company' => '/news/fast-company.html', + 'Engadget' => '/news/engadget.html', + 'Wired' => '/news/wired.html', + 'MakeUseOf' => '/news/makeuseof.html', + 'Techcrunch' => '/news/techcrunch.html', + 'Apple Insider' => '/news/apple-insider.html', + 'ArsTechnica' => '/news/arstechnica.html', + 'Tech in Asia' => '/news/tech-in-asia.html', + 'FastCoExist' => '/news/fastcoexist.html', + 'Digital Trends' => '/news/digital-trends.html', + 'AnandTech' => '/news/anandtech.html', + 'How to Geek' => '/news/how-to-geek.html', + 'Geek' => '/news/geek.html', + 'BBC Technology' => '/news/bbc-technology.html', + 'Extreme Tech' => '/news/extreme-tech.html', + 'Packet Storm Sec' => '/news/packet-storm-sec.html', + 'MedGadget' => '/news/medgadget.html', + 'Design' => '/news/design.html', + 'The Next Web' => '/news/the-next-web.html', + 'Bit-Tech' => '/news/bit-tech.html', + 'Next Big Future' => '/news/next-big-future.html', + 'A VC' => '/news/a-vc.html', + 'Copyblogger' => '/news/copyblogger.html', + 'Smashing Mag' => '/news/smashing-mag.html', + 'Continuations' => '/news/continuations.html', + 'Cult of Mac' => '/news/cult-of-mac.html', + 'SecuriTeam' => '/news/securiteam.html', + 'The Tech Block' => '/news/the-tech-block.html', + 'BetaBeat' => '/news/betabeat.html', + 'PC Mag' => '/news/pc-mag.html', + 'Venture Beat' => '/news/venture-beat.html', + 'ReadWriteWeb' => '/news/readwriteweb.html', + 'High Scalability' => '/news/high-scalability.html', + ) + ) + ), + self::CONTEXT_HOT_TOPICS => array(), + self::CONTEXT_TECH_NEWS => array( // auto-generated (see below) + 'tech_channel' => array( + 'name' => 'Tech channel', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your tech channel', + 'values' => array( + 'Agg' => array( + 'Reddit' => '/news/reddit.html', + 'Tech Insider' => '/news/tech-insider.html', + 'Digg' => '/news/digg.html', + 'Meta Filter' => '/news/meta-filter.html', + 'Fark' => '/news/fark.html', + 'Mashable' => '/news/mashable.html', + 'Ad Week' => '/news/ad-week.html', + 'The Chive' => '/news/the-chive.html', + 'BoingBoing' => '/news/boingboing.html', + 'Vice' => '/news/vice.html', + 'ClientsFromHell' => '/news/clientsfromhell.html', + 'How Stuff Works' => '/news/how-stuff-works.html', + 'Buzzfeed' => '/news/buzzfeed.html', + 'BoingBoing' => '/news/boingboing.html', + 'Cracked' => '/news/cracked.html', + 'Weird News' => '/news/weird-news.html', + 'ITOTD' => '/news/itotd.html', + 'Metafilter' => '/news/metafilter.html', + 'TheOnion' => '/news/theonion.html', + ), + 'Cars' => array( + 'Reddit Cars' => '/news/reddit-cars.html', + 'NYT Auto' => '/news/nyt-auto.html', + 'Truth About Cars' => '/news/truth-about-cars.html', + 'AutoBlog' => '/news/autoblog.html', + 'AutoSpies' => '/news/autospies.html', + 'Autoweek' => '/news/autoweek.html', + 'The Garage' => '/news/the-garage.html', + 'Car and Driver' => '/news/car-and-driver.html', + 'EGM Car Tech' => '/news/egm-car-tech.html', + 'Top Gear' => '/news/top-gear.html', + 'eGarage' => '/news/egarage.html', + ), + 'Comics' => array( + 'Penny Arcade' => '/news/penny-arcade.html', + 'XKCD' => '/news/xkcd.html', + 'Channelate' => '/news/channelate.html', + 'Savage Chicken' => '/news/savage-chicken.html', + 'Dinosaur Comics' => '/news/dinosaur-comics.html', + 'Explosm' => '/news/explosm.html', + 'PoorlyDLines' => '/news/poorlydlines.html', + 'Moonbeard' => '/news/moonbeard.html', + 'Nedroid' => '/news/nedroid.html', + ), + 'Design' => array( + 'FastCoCreate' => '/news/fastcocreate.html', + 'Dezeen' => '/news/dezeen.html', + 'Design Boom' => '/news/design-boom.html', + 'Mmminimal' => '/news/mmminimal.html', + 'We Heart' => '/news/we-heart.html', + 'CreativeBloq' => '/news/creativebloq.html', + 'TheDSGNblog' => '/news/thedsgnblog.html', + 'Grainedit' => '/news/grainedit.html', + ), + 'Football' => array( + 'Mail Football' => '/news/mail-football.html', + 'Yahoo Football' => '/news/yahoo-football.html', + 'FourFourTwo' => '/news/fourfourtwo.html', + 'Goal' => '/news/goal.html', + 'BBC Football' => '/news/bbc-football.html', + 'TalkSport' => '/news/talksport.html', + '101 Great Goals' => '/news/101-great-goals.html', + 'Who Scored' => '/news/who-scored.html', + 'Football365 Champ' => '/news/football365-champ.html', + 'Football365 Premier' => '/news/football365-premier.html', + 'BleacherReport' => '/news/bleacherreport.html', + ), + 'Gaming' => array( + 'Polygon' => '/news/polygon.html', + 'Gamespot' => '/news/gamespot.html', + 'RockPaperShotgun' => '/news/rockpapershotgun.html', + 'VG247' => '/news/vg247.html', + 'IGN' => '/news/ign.html', + 'Reddit Games' => '/news/reddit-games.html', + 'TouchArcade' => '/news/toucharcade.html', + 'GamesRadar' => '/news/gamesradar.html', + 'Siliconera' => '/news/siliconera.html', + 'Reddit GameDeals' => '/news/reddit-gamedeals.html', + 'Joystiq' => '/news/joystiq.html', + 'GameInformer' => '/news/gameinformer.html', + 'PSN Blog' => '/news/psn-blog.html', + 'Reddit GamerNews' => '/news/reddit-gamernews.html', + 'Steam' => '/news/steam.html', + 'DualShockers' => '/news/dualshockers.html', + 'ShackNews' => '/news/shacknews.html', + 'CheapAssGamer' => '/news/cheapassgamer.html', + 'Eurogamer' => '/news/eurogamer.html', + 'Major Nelson' => '/news/major-nelson.html', + 'Reddit Truegaming' => '/news/reddit-truegaming.html', + 'GameTrailers' => '/news/gametrailers.html', + 'GamaSutra' => '/news/gamasutra.html', + 'USGamer' => '/news/usgamer.html', + 'Shoryuken' => '/news/shoryuken.html', + 'Destructoid' => '/news/destructoid.html', + 'ArsGaming' => '/news/arsgaming.html', + 'XBOX Blog' => '/news/xbox-blog.html', + 'GiantBomb' => '/news/giantbomb.html', + 'VideoGamer' => '/news/videogamer.html', + 'Pocket Tactics' => '/news/pocket-tactics.html', + 'WiredGaming' => '/news/wiredgaming.html', + 'AllGamesBeta' => '/news/allgamesbeta.html', + 'OnGamers' => '/news/ongamers.html', + 'Reddit GameBundles' => '/news/reddit-gamebundles.html', + 'Kotaku' => '/news/kotaku.html', + 'PCGamer' => '/news/pcgamer.html', + ), + 'Investing' => array( + 'Seeking Alpha' => '/news/seeking-alpha.html', + 'BBC Business' => '/news/bbc-business.html', + 'Harvard Biz' => '/news/harvard-biz.html', + 'Market Watch' => '/news/market-watch.html', + 'Investor Place' => '/news/investor-place.html', + 'Money Week' => '/news/money-week.html', + 'Moneybeat' => '/news/moneybeat.html', + 'Dealbook' => '/news/dealbook.html', + 'Economist Business' => '/news/economist-business.html', + 'Economist' => '/news/economist.html', + 'Economist CN' => '/news/economist-cn.html', + ), + 'Long' => array( + 'The Atlantic' => '/news/the-atlantic.html', + 'Reddit Long' => '/news/reddit-long.html', + 'Paris Review' => '/news/paris-review.html', + 'New Yorker' => '/news/new-yorker.html', + 'LongForm' => '/news/longform.html', + 'LongReads' => '/news/longreads.html', + 'The Browser' => '/news/the-browser.html', + 'The Feature' => '/news/the-feature.html', + ), + 'MMA' => array( + 'MMA Weekly' => '/news/mma-weekly.html', + 'MMAFighting' => '/news/mmafighting.html', + 'Reddit MMA' => '/news/reddit-mma.html', + 'Sherdog Articles' => '/news/sherdog-articles.html', + 'FightLand Vice' => '/news/fightland-vice.html', + 'Sherdog Forum' => '/news/sherdog-forum.html', + 'MMA Junkie' => '/news/mma-junkie.html', + 'Sherdog MMA Video' => '/news/sherdog-mma-video.html', + 'BloodyElbow' => '/news/bloodyelbow.html', + 'CageWriter' => '/news/cagewriter.html', + 'Sherdog News' => '/news/sherdog-news.html', + 'MMAForum' => '/news/mmaforum.html', + 'MMA Junkie Radio' => '/news/mma-junkie-radio.html', + 'UFC News' => '/news/ufc-news.html', + 'FightLinker' => '/news/fightlinker.html', + 'Bodybuilding MMA' => '/news/bodybuilding-mma.html', + 'BleacherReport MMA' => '/news/bleacherreport-mma.html', + 'FiveOuncesofPain' => '/news/fiveouncesofpain.html', + 'Sherdog Pictures' => '/news/sherdog-pictures.html', + 'CagePotato' => '/news/cagepotato.html', + 'Sherdog Radio' => '/news/sherdog-radio.html', + 'ProMMARadio' => '/news/prommaradio.html', + ), + 'Mobile' => array( + 'Macrumors' => '/news/macrumors.html', + 'Android Police' => '/news/android-police.html', + 'GSM Arena' => '/news/gsm-arena.html', + 'DigiTrend Mobile' => '/news/digitrend-mobile.html', + 'Mobile Nation' => '/news/mobile-nation.html', + 'TechRadar' => '/news/techradar.html', + 'ZDNET Mobile' => '/news/zdnet-mobile.html', + 'MacWorld' => '/news/macworld.html', + 'Android Dev Blog' => '/news/android-dev-blog.html', + ), + 'News' => array( + 'Daily Mail' => '/news/daily-mail.html', + 'Business Insider' => '/news/business-insider.html', + 'The Guardian' => '/news/the-guardian.html', + 'Fox' => '/news/fox.html', + 'BBC World' => '/news/bbc-world.html', + 'MSNBC' => '/news/msnbc.html', + 'ABC News' => '/news/abc-news.html', + 'Al Jazeera' => '/news/al-jazeera.html', + 'Business Insider India' => '/news/business-insider-india.html', + 'Observer' => '/news/observer.html', + 'NYT Tech' => '/news/nyt-tech.html', + 'NYT World' => '/news/nyt-world.html', + 'CNN' => '/news/cnn.html', + 'Japan Times' => '/news/japan-times.html', + 'WorldCrunch' => '/news/worldcrunch.html', + 'Pro publica' => '/news/pro-publica.html', + 'OZY' => '/news/ozy.html', + 'Times of India' => '/news/times-of-india.html', + 'The Australian' => '/news/the-australian.html', + 'Harpers' => '/news/harpers.html', + 'Moscow Times' => '/news/moscow-times.html', + 'The Times' => '/news/the-times.html', + 'Reuters Tech' => '/news/reuters-tech.html', + ), + 'Politics' => array( + 'FreeRepublic' => '/news/freerepublic.html', + 'Salon' => '/news/salon.html', + 'DrudgeReport' => '/news/drudgereport.html', + 'TheHill' => '/news/thehill.html', + 'TheBlaze' => '/news/theblaze.html', + 'InfoWars' => '/news/infowars.html', + 'New Republic' => '/news/new-republic.html', + 'WashTimes' => '/news/washtimes.html', + 'RealCleanPol' => '/news/realcleanpol.html', + 'Fact Check' => '/news/fact-check.html', + 'DailyKos' => '/news/dailykos.html', + 'NewsMax' => '/news/newsmax.html', + 'Politico' => '/news/politico.html', + 'Michelle Malkin' => '/news/michelle-malkin.html', + ), + 'Reddit' => array( + 'R Movies' => '/news/r-movies.html', + 'R News' => '/news/r-news.html', + 'Futurology' => '/news/futurology.html', + 'R All' => '/news/r-all.html', + 'R Music' => '/news/r-music.html', + 'R Askscience' => '/news/r-askscience.html', + 'R Technology' => '/news/r-technology.html', + 'R Bestof' => '/news/r-bestof.html', + 'R Askreddit' => '/news/r-askreddit.html', + 'R Worldnews' => '/news/r-worldnews.html', + 'R Explainlikeimfive' => '/news/r-explainlikeimfive.html', + 'R Iama' => '/news/r-iama.html', + ), + 'Science' => array( + 'PhysOrg' => '/news/physorg.html', + 'Hack-a-day' => '/news/hack-a-day.html', + 'Reddit Science' => '/news/reddit-science.html', + 'Stats Blog' => '/news/stats-blog.html', + 'Flowing Data' => '/news/flowing-data.html', + 'Eureka Alert' => '/news/eureka-alert.html', + 'Robotics BizRev' => '/news/robotics-bizrev.html', + 'Planet big Data' => '/news/planet-big-data.html', + 'Makezine' => '/news/makezine.html', + 'MIT Tech' => '/news/mit-tech.html', + 'R Bloggers' => '/news/r-bloggers.html', + 'DataIsBeautiful' => '/news/dataisbeautiful.html', + 'Ted Videos' => '/news/ted-videos.html', + 'Advanced Science' => '/news/advanced-science.html', + 'Robotiq' => '/news/robotiq.html', + 'Science Daily' => '/news/science-daily.html', + 'IEEE Robotics' => '/news/ieee-robotics.html', + 'PSFK' => '/news/psfk.html', + 'Discover Magazine' => '/news/discover-magazine.html', + 'DataTau' => '/news/datatau.html', + 'RoboHub' => '/news/robohub.html', + 'Discovery' => '/news/discovery.html', + 'Smart Data' => '/news/smart-data.html', + 'Whats Big Data' => '/news/whats-big-data.html', + ), + 'Tech' => array( + 'Hacker News' => '/news/hacker-news.html', + 'The Verge' => '/news/the-verge.html', + 'Lifehacker' => '/news/lifehacker.html', + 'Fast Company' => '/news/fast-company.html', + 'ArsTechnica' => '/news/arstechnica.html', + 'MakeUseOf' => '/news/makeuseof.html', + 'FastCoExist' => '/news/fastcoexist.html', + 'How to Geek' => '/news/how-to-geek.html', + 'The Next Web' => '/news/the-next-web.html', + 'Engadget' => '/news/engadget.html', + 'Gizmag' => '/news/gizmag.html', + 'QZ' => '/news/qz.html', + 'Wired' => '/news/wired.html', + 'Techcrunch' => '/news/techcrunch.html', + 'Slashdot' => '/news/slashdot.html', + 'Extreme Tech' => '/news/extreme-tech.html', + 'AnandTech' => '/news/anandtech.html', + 'Digital Trends' => '/news/digital-trends.html', + 'Next Big Future' => '/news/next-big-future.html', + 'Apple Insider' => '/news/apple-insider.html', + 'Geek' => '/news/geek.html', + 'BBC Technology' => '/news/bbc-technology.html', + 'Bit-Tech' => '/news/bit-tech.html', + 'Packet Storm Sec' => '/news/packet-storm-sec.html', + 'Design' => '/news/design.html', + 'High Scalability' => '/news/high-scalability.html', + 'Smashing Mag' => '/news/smashing-mag.html', + 'The Tech Block' => '/news/the-tech-block.html', + 'A VC' => '/news/a-vc.html', + 'Tech in Asia' => '/news/tech-in-asia.html', + 'ReadWriteWeb' => '/news/readwriteweb.html', + 'PC Mag' => '/news/pc-mag.html', + 'Continuations' => '/news/continuations.html', + 'Copyblogger' => '/news/copyblogger.html', + 'Cult of Mac' => '/news/cult-of-mac.html', + 'BetaBeat' => '/news/betabeat.html', + 'MedGadget' => '/news/medgadget.html', + 'SecuriTeam' => '/news/securiteam.html', + 'Venture Beat' => '/news/venture-beat.html', + ), + 'Trend' => array( + 'Trend Hunter' => '/news/trend-hunter.html', + 'ApartmentT' => '/news/apartmentt.html', + 'GQ' => '/news/gq.html', + 'Digital Trends' => '/news/digital-trends.html', + 'Cool Hunting' => '/news/cool-hunting.html', + 'FastCoDesign' => '/news/fastcodesign.html', + 'TC Startups' => '/news/tc-startups.html', + 'Killer Startups' => '/news/killer-startups.html', + 'DigiInfo' => '/news/digiinfo.html', + 'New Startups' => '/news/new-startups.html', + 'DigiTrends' => '/news/digitrends.html', + ), + 'Watches' => array( + 'Hodinkee' => '/news/hodinkee.html', + 'Quill and Pad' => '/news/quill-and-pad.html', + 'Monochrome' => '/news/monochrome.html', + 'Deployant' => '/news/deployant.html', + 'Watches by SJX' => '/news/watches-by-sjx.html', + 'Fratello Watches' => '/news/fratello-watches.html', + 'A Blog to Watch' => '/news/a-blog-to-watch.html', + 'Wound for Life' => '/news/wound-for-life.html', + 'Watch Paper' => '/news/watch-paper.html', + 'Watch Report' => '/news/watch-report.html', + 'Perpetuelle' => '/news/perpetuelle.html', + ), + 'Youtube' => array( + 'LinusTechTips' => '/news/linustechtips.html', + 'MetalJesusRocks' => '/news/metaljesusrocks.html', + 'TotalBiscuit' => '/news/totalbiscuit.html', + 'DexBonus' => '/news/dexbonus.html', + 'Lon Siedman' => '/news/lon-siedman.html', + 'MKBHD' => '/news/mkbhd.html', + 'Terry A Davis' => '/news/terry-a-davis.html', + 'HappyConsole' => '/news/happyconsole.html', + 'Austin Evans' => '/news/austin-evans.html', + 'NCIX' => '/news/ncix.html', + ), + ) + ), + ), + self::CONTEXT_CUSTOM => array( + 'config' => array( + 'name' => 'Configuration', + 'type' => 'text', + 'required' => true, + 'title' => 'Enter feed numbers from Skimfeed!', + 'exampleValue' => '5,8,2,l,p,9,23' + ) + ), + 'global' => array( + 'limit' => array( + 'name' => 'Limit', + 'type' => 'number', + 'title' => 'Limits the number of returned items in the feed', + 'exampleValue' => 10 + ) + ) + ); + + public function getURI() { + + switch($this->queriedContext) { + + case self::CONTEXT_NEWS_BOX: + + $channel = $this->getInput('box_channel'); + + if($channel) { + return static::URI . $channel; + } + + break; + + case self::CONTEXT_HOT_TOPICS: + return static::URI; + + case self::CONTEXT_TECH_NEWS: + + $channel = $this->getInput('tech_channel'); + + if($channel) { + return static::URI . $channel; + } + + break; + + case self::CONTEXT_CUSTOM: + + $config = $this->getInput('config'); + + return static::URI . '/custom.php?f=' . urlencode($config); + + } + + return parent::getURI(); + + } + + public function getName() { + + switch($this->queriedContext) { + + case self::CONTEXT_NEWS_BOX: + + $channel = $this->getInput('box_channel'); + + $title = array_search( + $channel, + static::PARAMETERS[self::CONTEXT_NEWS_BOX]['box_channel']['values'] + ); + + return $title . ' - ' . static::NAME; + + case self::CONTEXT_HOT_TOPICS: + return 'Hot topics - ' . static::NAME; + + case self::CONTEXT_TECH_NEWS: + + $channel = $this->getInput('tech_channel'); + + $titles = array(); + + foreach(static::PARAMETERS[self::CONTEXT_TECH_NEWS]['tech_channel']['values'] as $ch) { + $titles = array_merge($titles, $ch); + } + + $title = array_search($channel, $titles); + + return $title . ' - ' . static::NAME; + + case self::CONTEXT_CUSTOM: + return 'Custom - ' . static::NAME; + + } + + return parent::getName(); + + } + + public function collectData() { + + // enable to export parameter lists + // $this->exportBoxChannels(); die; + // $this->exportTechChannels(); die; + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Request to ' . $this->getURI() . ' failed!'); + + defaultLinkTo($html, static::URI); + + switch($this->queriedContext) { + + case self::CONTEXT_NEWS_BOX: + + $author = array_search( + $this->getInput('box_channel'), + static::PARAMETERS[self::CONTEXT_NEWS_BOX]['box_channel']['values'] + ); + + $author = '' + . $author + . ''; + + $this->extractFeed($html, $author); + break; + + case self::CONTEXT_HOT_TOPICS: + $this->extractHotTopics($html); + break; + + case self::CONTEXT_TECH_NEWS: + $authors = array(); + + foreach(static::PARAMETERS[self::CONTEXT_TECH_NEWS]['tech_channel']['values'] as $ch) { + $authors = array_merge($authors, $ch); + } + + $author = '' + . array_search($this->getInput('tech_channel'), $authors) + . ''; + + $this->extractFeed($html, $author); + break; + + case self::CONTEXT_CUSTOM: + $this->extractCustomFeed($html); + break; + + } + + } + + private function extractFeed($html, $author) { + + $articles = $html->find('li') + or returnServerError('Could not find articles!'); + + if(count($articles) === 1 + && stristr($articles[0]->plaintext, 'Nothing new in the last 48 hours')) { + return; // Nothing to show + } + + $limit = $this->getInput('limit') ?: -1; + + foreach($articles as $article) { + + $anchor = $article->find('a', 0) + or returnServerError('Could not find anchor!'); + + $item = array(); + + $item['uri'] = $this->getTarget($anchor); + $item['title'] = trim($anchor->plaintext); + + // The timestamp is encoded as relative time (max. the last 48 hours) + // like this: "- 7 hours". It should always be at the end of the article: + $age = substr($article->plaintext, strrpos($article->plaintext, '-')); + + $item['timestamp'] = strtotime($age); + $item['author'] = $author; + + $this->items[] = $item; + + if($limit > 0 && count($this->items) >= $limit) { + return; + } + + } + + } + + private function extractHotTopics($html) { + + $topics = $html->find('#popbox ul li') + or returnServerError('Could not find topics!'); + + $limit = $this->getInput('limit') ?: -1; + + foreach($topics as $topic) { + + $anchor = $topic->find('a', 0) + or returnServerError('Could not find anchor!'); + + $item = array(); + + $item['uri'] = $this->getTarget($anchor); + $item['title'] = $anchor->title; + + $this->items[] = $item; + + if($limit > 0 && count($this->items) >= $limit) { + return; + } + + } + + } + + private function extractCustomFeed($html) { + + $boxes = $html->find('#boxx .boxes') + or returnServerError('Could not find boxes!'); + + foreach($boxes as $box) { + + $anchor = $box->find('span.boxtitles a', 0) + or returnServerError('Could not find box anchor!'); + + $author = '' . trim($anchor->plaintext) . ''; + $uri = $anchor->href; + + $box_html = getSimpleHTMLDOM($uri) + or returnServerError('Could not load custom feed!'); + + $this->extractFeed($box_html, $author); + + } + + } + + private function getTarget($anchor) { + + // Anchors are linked to Skimfeed, luckily the target URI is encoded + // in that URI via '&u=': + $query = parse_url($anchor->href, PHP_URL_QUERY); + + foreach(explode('&', $query) as $parameter) { + + list($key, $value) = explode('=', $parameter); + + if($key !== 'u') { + continue; + } + + return urldecode($value); + + } + + } + + /** + * dev-mode! + * Requires '&format=Html' + * + * Returns the 'box' array from the source site + */ + private function exportBoxChannels() { + $html = getSimpleHTMLDOMCached(static::URI) + or returnServerError('No contents received from Skimfeed!'); + + if(!$this->isCompatible($html)) { + returnServerError('Skimfeed version is not compatible!'); + } + + $boxes = $html->find('#boxx .boxes') + or returnServerError('Could not find boxes!'); + + // begin of 'channel' list + $message = << array( + 'name' => 'Channel', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your channel', + 'values' => array( + +EOD; + + foreach($boxes as $box) { + + $anchor = $box->find('span.boxtitles a', 0) + or returnServerError('Could not find box anchor!'); + + $title = trim($anchor->plaintext); + $uri = $anchor->href; + + // add value + $message .= "\t\t'{$title}' => '{$uri}', \n"; + + } + + // end of 'box' list + $message .= << + + + + {$message} + + +EOD; + + } + + /** + * dev-mode! + * Requires '&format=Html' + * + * Returns the 'techs' array from the source site + */ + private function exportTechChannels() { + $html = getSimpleHTMLDOMCached(static::URI) + or returnServerError('No contents received from Skimfeed!'); + + if(!$this->isCompatible($html)) { + returnServerError('Skimfeed version is not compatible!'); + } + + $channels = $html->find('#menubar a') + or returnServerError('Could not find channels!'); + + // begin of 'tech_channel' list + $message = << array( + 'name' => 'Tech channel', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your tech channel', + 'values' => array( + +EOD; + + foreach($channels as $channel) { + + if($channel->href === '#' + || $channel->class === 'homelink' + || $channel->plaintext === 'Twitter' + || $channel->plaintext === 'Weather' + || $channel->plaintext === '+Custom') { + continue; + } + + $title = trim($channel->plaintext); + $uri = '/' . $channel->href; + + $message .= "\t\t'{$title}' => array(\n"; + + $channel_html = getSimpleHTMLDOMCached(static::URI . $uri) + or returnServerError('Could not load tech channel ' . $channel->plaintext . '!'); + + $boxes = $channel_html->find('#boxx .boxes') + or returnServerError('Could not find boxes!'); + + foreach($boxes as $box) { + + $anchor = $box->find('span.boxtitles a', 0) + or returnServerError('Could not find box anchor!'); + + $boxtitle = trim($anchor->plaintext); + $boxuri = $anchor->href; + + $message .= "\t\t\t'{$boxtitle}' => '{$boxuri}', \n"; + + } + + $message .= "\t\t),\n"; + + } + + // end of 'box' list + $message .= << + + + + {$message} + + +EOD; + } + + + /** + * Checks if the reported skimfeed version is compatible + */ + private function isCompatible($html) { + $title = $html->find('title', 0); + + if(!$title) { + return false; + } + + if($title->plaintext === 'Skimfeed V5.5 - Tech News') { + return true; + } + + return false; + } + +}