SkimfeedBridge.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. <?php
  2. class SkimfeedBridge extends BridgeAbstract {
  3. const CONTEXT_NEWS_BOX = 'News box';
  4. const CONTEXT_HOT_TOPICS = 'Hot topics';
  5. const CONTEXT_TECH_NEWS = 'Tech news';
  6. const CONTEXT_CUSTOM = 'Custom feed';
  7. const NAME = 'Skimfeed Bridge';
  8. const URI = 'https://skimfeed.com';
  9. const DESCRIPTION = 'Returns feeds from Skimfeed, also supports custom feeds!';
  10. const MAINTAINER = 'logmanoriginal';
  11. const CACHE_TIMEOUT = 3600;
  12. const PARAMETERS = array(
  13. self::CONTEXT_NEWS_BOX => array( // auto-generated (see below)
  14. 'box_channel' => array(
  15. 'name' => 'Channel',
  16. 'type' => 'list',
  17. 'required' => true,
  18. 'title' => 'Select your channel',
  19. 'values' => array(
  20. 'Hacker News' => '/news/hacker-news.html',
  21. 'QZ' => '/news/qz.html',
  22. 'The Verge' => '/news/the-verge.html',
  23. 'Slashdot' => '/news/slashdot.html',
  24. 'Lifehacker' => '/news/lifehacker.html',
  25. 'Gizmag' => '/news/gizmag.html',
  26. 'Fast Company' => '/news/fast-company.html',
  27. 'Engadget' => '/news/engadget.html',
  28. 'Wired' => '/news/wired.html',
  29. 'MakeUseOf' => '/news/makeuseof.html',
  30. 'Techcrunch' => '/news/techcrunch.html',
  31. 'Apple Insider' => '/news/apple-insider.html',
  32. 'ArsTechnica' => '/news/arstechnica.html',
  33. 'Tech in Asia' => '/news/tech-in-asia.html',
  34. 'FastCoExist' => '/news/fastcoexist.html',
  35. 'Digital Trends' => '/news/digital-trends.html',
  36. 'AnandTech' => '/news/anandtech.html',
  37. 'How to Geek' => '/news/how-to-geek.html',
  38. 'Geek' => '/news/geek.html',
  39. 'BBC Technology' => '/news/bbc-technology.html',
  40. 'Extreme Tech' => '/news/extreme-tech.html',
  41. 'Packet Storm Sec' => '/news/packet-storm-sec.html',
  42. 'MedGadget' => '/news/medgadget.html',
  43. 'Design' => '/news/design.html',
  44. 'The Next Web' => '/news/the-next-web.html',
  45. 'Bit-Tech' => '/news/bit-tech.html',
  46. 'Next Big Future' => '/news/next-big-future.html',
  47. 'A VC' => '/news/a-vc.html',
  48. 'Copyblogger' => '/news/copyblogger.html',
  49. 'Smashing Mag' => '/news/smashing-mag.html',
  50. 'Continuations' => '/news/continuations.html',
  51. 'Cult of Mac' => '/news/cult-of-mac.html',
  52. 'SecuriTeam' => '/news/securiteam.html',
  53. 'The Tech Block' => '/news/the-tech-block.html',
  54. 'BetaBeat' => '/news/betabeat.html',
  55. 'PC Mag' => '/news/pc-mag.html',
  56. 'Venture Beat' => '/news/venture-beat.html',
  57. 'ReadWriteWeb' => '/news/readwriteweb.html',
  58. 'High Scalability' => '/news/high-scalability.html',
  59. )
  60. )
  61. ),
  62. self::CONTEXT_HOT_TOPICS => array(),
  63. self::CONTEXT_TECH_NEWS => array( // auto-generated (see below)
  64. 'tech_channel' => array(
  65. 'name' => 'Tech channel',
  66. 'type' => 'list',
  67. 'required' => true,
  68. 'title' => 'Select your tech channel',
  69. 'values' => array(
  70. 'Agg' => array(
  71. 'Reddit' => '/news/reddit.html',
  72. 'Tech Insider' => '/news/tech-insider.html',
  73. 'Digg' => '/news/digg.html',
  74. 'Meta Filter' => '/news/meta-filter.html',
  75. 'Fark' => '/news/fark.html',
  76. 'Mashable' => '/news/mashable.html',
  77. 'Ad Week' => '/news/ad-week.html',
  78. 'The Chive' => '/news/the-chive.html',
  79. 'BoingBoing' => '/news/boingboing.html',
  80. 'Vice' => '/news/vice.html',
  81. 'ClientsFromHell' => '/news/clientsfromhell.html',
  82. 'How Stuff Works' => '/news/how-stuff-works.html',
  83. 'Buzzfeed' => '/news/buzzfeed.html',
  84. 'BoingBoing' => '/news/boingboing.html',
  85. 'Cracked' => '/news/cracked.html',
  86. 'Weird News' => '/news/weird-news.html',
  87. 'ITOTD' => '/news/itotd.html',
  88. 'Metafilter' => '/news/metafilter.html',
  89. 'TheOnion' => '/news/theonion.html',
  90. ),
  91. 'Cars' => array(
  92. 'Reddit Cars' => '/news/reddit-cars.html',
  93. 'NYT Auto' => '/news/nyt-auto.html',
  94. 'Truth About Cars' => '/news/truth-about-cars.html',
  95. 'AutoBlog' => '/news/autoblog.html',
  96. 'AutoSpies' => '/news/autospies.html',
  97. 'Autoweek' => '/news/autoweek.html',
  98. 'The Garage' => '/news/the-garage.html',
  99. 'Car and Driver' => '/news/car-and-driver.html',
  100. 'EGM Car Tech' => '/news/egm-car-tech.html',
  101. 'Top Gear' => '/news/top-gear.html',
  102. 'eGarage' => '/news/egarage.html',
  103. ),
  104. 'Comics' => array(
  105. 'Penny Arcade' => '/news/penny-arcade.html',
  106. 'XKCD' => '/news/xkcd.html',
  107. 'Channelate' => '/news/channelate.html',
  108. 'Savage Chicken' => '/news/savage-chicken.html',
  109. 'Dinosaur Comics' => '/news/dinosaur-comics.html',
  110. 'Explosm' => '/news/explosm.html',
  111. 'PoorlyDLines' => '/news/poorlydlines.html',
  112. 'Moonbeard' => '/news/moonbeard.html',
  113. 'Nedroid' => '/news/nedroid.html',
  114. ),
  115. 'Design' => array(
  116. 'FastCoCreate' => '/news/fastcocreate.html',
  117. 'Dezeen' => '/news/dezeen.html',
  118. 'Design Boom' => '/news/design-boom.html',
  119. 'Mmminimal' => '/news/mmminimal.html',
  120. 'We Heart' => '/news/we-heart.html',
  121. 'CreativeBloq' => '/news/creativebloq.html',
  122. 'TheDSGNblog' => '/news/thedsgnblog.html',
  123. 'Grainedit' => '/news/grainedit.html',
  124. ),
  125. 'Football' => array(
  126. 'Mail Football' => '/news/mail-football.html',
  127. 'Yahoo Football' => '/news/yahoo-football.html',
  128. 'FourFourTwo' => '/news/fourfourtwo.html',
  129. 'Goal' => '/news/goal.html',
  130. 'BBC Football' => '/news/bbc-football.html',
  131. 'TalkSport' => '/news/talksport.html',
  132. '101 Great Goals' => '/news/101-great-goals.html',
  133. 'Who Scored' => '/news/who-scored.html',
  134. 'Football365 Champ' => '/news/football365-champ.html',
  135. 'Football365 Premier' => '/news/football365-premier.html',
  136. 'BleacherReport' => '/news/bleacherreport.html',
  137. ),
  138. 'Gaming' => array(
  139. 'Polygon' => '/news/polygon.html',
  140. 'Gamespot' => '/news/gamespot.html',
  141. 'RockPaperShotgun' => '/news/rockpapershotgun.html',
  142. 'VG247' => '/news/vg247.html',
  143. 'IGN' => '/news/ign.html',
  144. 'Reddit Games' => '/news/reddit-games.html',
  145. 'TouchArcade' => '/news/toucharcade.html',
  146. 'GamesRadar' => '/news/gamesradar.html',
  147. 'Siliconera' => '/news/siliconera.html',
  148. 'Reddit GameDeals' => '/news/reddit-gamedeals.html',
  149. 'Joystiq' => '/news/joystiq.html',
  150. 'GameInformer' => '/news/gameinformer.html',
  151. 'PSN Blog' => '/news/psn-blog.html',
  152. 'Reddit GamerNews' => '/news/reddit-gamernews.html',
  153. 'Steam' => '/news/steam.html',
  154. 'DualShockers' => '/news/dualshockers.html',
  155. 'ShackNews' => '/news/shacknews.html',
  156. 'CheapAssGamer' => '/news/cheapassgamer.html',
  157. 'Eurogamer' => '/news/eurogamer.html',
  158. 'Major Nelson' => '/news/major-nelson.html',
  159. 'Reddit Truegaming' => '/news/reddit-truegaming.html',
  160. 'GameTrailers' => '/news/gametrailers.html',
  161. 'GamaSutra' => '/news/gamasutra.html',
  162. 'USGamer' => '/news/usgamer.html',
  163. 'Shoryuken' => '/news/shoryuken.html',
  164. 'Destructoid' => '/news/destructoid.html',
  165. 'ArsGaming' => '/news/arsgaming.html',
  166. 'XBOX Blog' => '/news/xbox-blog.html',
  167. 'GiantBomb' => '/news/giantbomb.html',
  168. 'VideoGamer' => '/news/videogamer.html',
  169. 'Pocket Tactics' => '/news/pocket-tactics.html',
  170. 'WiredGaming' => '/news/wiredgaming.html',
  171. 'AllGamesBeta' => '/news/allgamesbeta.html',
  172. 'OnGamers' => '/news/ongamers.html',
  173. 'Reddit GameBundles' => '/news/reddit-gamebundles.html',
  174. 'Kotaku' => '/news/kotaku.html',
  175. 'PCGamer' => '/news/pcgamer.html',
  176. ),
  177. 'Investing' => array(
  178. 'Seeking Alpha' => '/news/seeking-alpha.html',
  179. 'BBC Business' => '/news/bbc-business.html',
  180. 'Harvard Biz' => '/news/harvard-biz.html',
  181. 'Market Watch' => '/news/market-watch.html',
  182. 'Investor Place' => '/news/investor-place.html',
  183. 'Money Week' => '/news/money-week.html',
  184. 'Moneybeat' => '/news/moneybeat.html',
  185. 'Dealbook' => '/news/dealbook.html',
  186. 'Economist Business' => '/news/economist-business.html',
  187. 'Economist' => '/news/economist.html',
  188. 'Economist CN' => '/news/economist-cn.html',
  189. ),
  190. 'Long' => array(
  191. 'The Atlantic' => '/news/the-atlantic.html',
  192. 'Reddit Long' => '/news/reddit-long.html',
  193. 'Paris Review' => '/news/paris-review.html',
  194. 'New Yorker' => '/news/new-yorker.html',
  195. 'LongForm' => '/news/longform.html',
  196. 'LongReads' => '/news/longreads.html',
  197. 'The Browser' => '/news/the-browser.html',
  198. 'The Feature' => '/news/the-feature.html',
  199. ),
  200. 'MMA' => array(
  201. 'MMA Weekly' => '/news/mma-weekly.html',
  202. 'MMAFighting' => '/news/mmafighting.html',
  203. 'Reddit MMA' => '/news/reddit-mma.html',
  204. 'Sherdog Articles' => '/news/sherdog-articles.html',
  205. 'FightLand Vice' => '/news/fightland-vice.html',
  206. 'Sherdog Forum' => '/news/sherdog-forum.html',
  207. 'MMA Junkie' => '/news/mma-junkie.html',
  208. 'Sherdog MMA Video' => '/news/sherdog-mma-video.html',
  209. 'BloodyElbow' => '/news/bloodyelbow.html',
  210. 'CageWriter' => '/news/cagewriter.html',
  211. 'Sherdog News' => '/news/sherdog-news.html',
  212. 'MMAForum' => '/news/mmaforum.html',
  213. 'MMA Junkie Radio' => '/news/mma-junkie-radio.html',
  214. 'UFC News' => '/news/ufc-news.html',
  215. 'FightLinker' => '/news/fightlinker.html',
  216. 'Bodybuilding MMA' => '/news/bodybuilding-mma.html',
  217. 'BleacherReport MMA' => '/news/bleacherreport-mma.html',
  218. 'FiveOuncesofPain' => '/news/fiveouncesofpain.html',
  219. 'Sherdog Pictures' => '/news/sherdog-pictures.html',
  220. 'CagePotato' => '/news/cagepotato.html',
  221. 'Sherdog Radio' => '/news/sherdog-radio.html',
  222. 'ProMMARadio' => '/news/prommaradio.html',
  223. ),
  224. 'Mobile' => array(
  225. 'Macrumors' => '/news/macrumors.html',
  226. 'Android Police' => '/news/android-police.html',
  227. 'GSM Arena' => '/news/gsm-arena.html',
  228. 'DigiTrend Mobile' => '/news/digitrend-mobile.html',
  229. 'Mobile Nation' => '/news/mobile-nation.html',
  230. 'TechRadar' => '/news/techradar.html',
  231. 'ZDNET Mobile' => '/news/zdnet-mobile.html',
  232. 'MacWorld' => '/news/macworld.html',
  233. 'Android Dev Blog' => '/news/android-dev-blog.html',
  234. ),
  235. 'News' => array(
  236. 'Daily Mail' => '/news/daily-mail.html',
  237. 'Business Insider' => '/news/business-insider.html',
  238. 'The Guardian' => '/news/the-guardian.html',
  239. 'Fox' => '/news/fox.html',
  240. 'BBC World' => '/news/bbc-world.html',
  241. 'MSNBC' => '/news/msnbc.html',
  242. 'ABC News' => '/news/abc-news.html',
  243. 'Al Jazeera' => '/news/al-jazeera.html',
  244. 'Business Insider India' => '/news/business-insider-india.html',
  245. 'Observer' => '/news/observer.html',
  246. 'NYT Tech' => '/news/nyt-tech.html',
  247. 'NYT World' => '/news/nyt-world.html',
  248. 'CNN' => '/news/cnn.html',
  249. 'Japan Times' => '/news/japan-times.html',
  250. 'WorldCrunch' => '/news/worldcrunch.html',
  251. 'Pro publica' => '/news/pro-publica.html',
  252. 'OZY' => '/news/ozy.html',
  253. 'Times of India' => '/news/times-of-india.html',
  254. 'The Australian' => '/news/the-australian.html',
  255. 'Harpers' => '/news/harpers.html',
  256. 'Moscow Times' => '/news/moscow-times.html',
  257. 'The Times' => '/news/the-times.html',
  258. 'Reuters Tech' => '/news/reuters-tech.html',
  259. ),
  260. 'Politics' => array(
  261. 'FreeRepublic' => '/news/freerepublic.html',
  262. 'Salon' => '/news/salon.html',
  263. 'DrudgeReport' => '/news/drudgereport.html',
  264. 'TheHill' => '/news/thehill.html',
  265. 'TheBlaze' => '/news/theblaze.html',
  266. 'InfoWars' => '/news/infowars.html',
  267. 'New Republic' => '/news/new-republic.html',
  268. 'WashTimes' => '/news/washtimes.html',
  269. 'RealCleanPol' => '/news/realcleanpol.html',
  270. 'Fact Check' => '/news/fact-check.html',
  271. 'DailyKos' => '/news/dailykos.html',
  272. 'NewsMax' => '/news/newsmax.html',
  273. 'Politico' => '/news/politico.html',
  274. 'Michelle Malkin' => '/news/michelle-malkin.html',
  275. ),
  276. 'Reddit' => array(
  277. 'R Movies' => '/news/r-movies.html',
  278. 'R News' => '/news/r-news.html',
  279. 'Futurology' => '/news/futurology.html',
  280. 'R All' => '/news/r-all.html',
  281. 'R Music' => '/news/r-music.html',
  282. 'R Askscience' => '/news/r-askscience.html',
  283. 'R Technology' => '/news/r-technology.html',
  284. 'R Bestof' => '/news/r-bestof.html',
  285. 'R Askreddit' => '/news/r-askreddit.html',
  286. 'R Worldnews' => '/news/r-worldnews.html',
  287. 'R Explainlikeimfive' => '/news/r-explainlikeimfive.html',
  288. 'R Iama' => '/news/r-iama.html',
  289. ),
  290. 'Science' => array(
  291. 'PhysOrg' => '/news/physorg.html',
  292. 'Hack-a-day' => '/news/hack-a-day.html',
  293. 'Reddit Science' => '/news/reddit-science.html',
  294. 'Stats Blog' => '/news/stats-blog.html',
  295. 'Flowing Data' => '/news/flowing-data.html',
  296. 'Eureka Alert' => '/news/eureka-alert.html',
  297. 'Robotics BizRev' => '/news/robotics-bizrev.html',
  298. 'Planet big Data' => '/news/planet-big-data.html',
  299. 'Makezine' => '/news/makezine.html',
  300. 'MIT Tech' => '/news/mit-tech.html',
  301. 'R Bloggers' => '/news/r-bloggers.html',
  302. 'DataIsBeautiful' => '/news/dataisbeautiful.html',
  303. 'Ted Videos' => '/news/ted-videos.html',
  304. 'Advanced Science' => '/news/advanced-science.html',
  305. 'Robotiq' => '/news/robotiq.html',
  306. 'Science Daily' => '/news/science-daily.html',
  307. 'IEEE Robotics' => '/news/ieee-robotics.html',
  308. 'PSFK' => '/news/psfk.html',
  309. 'Discover Magazine' => '/news/discover-magazine.html',
  310. 'DataTau' => '/news/datatau.html',
  311. 'RoboHub' => '/news/robohub.html',
  312. 'Discovery' => '/news/discovery.html',
  313. 'Smart Data' => '/news/smart-data.html',
  314. 'Whats Big Data' => '/news/whats-big-data.html',
  315. ),
  316. 'Tech' => array(
  317. 'Hacker News' => '/news/hacker-news.html',
  318. 'The Verge' => '/news/the-verge.html',
  319. 'Lifehacker' => '/news/lifehacker.html',
  320. 'Fast Company' => '/news/fast-company.html',
  321. 'ArsTechnica' => '/news/arstechnica.html',
  322. 'MakeUseOf' => '/news/makeuseof.html',
  323. 'FastCoExist' => '/news/fastcoexist.html',
  324. 'How to Geek' => '/news/how-to-geek.html',
  325. 'The Next Web' => '/news/the-next-web.html',
  326. 'Engadget' => '/news/engadget.html',
  327. 'Gizmag' => '/news/gizmag.html',
  328. 'QZ' => '/news/qz.html',
  329. 'Wired' => '/news/wired.html',
  330. 'Techcrunch' => '/news/techcrunch.html',
  331. 'Slashdot' => '/news/slashdot.html',
  332. 'Extreme Tech' => '/news/extreme-tech.html',
  333. 'AnandTech' => '/news/anandtech.html',
  334. 'Digital Trends' => '/news/digital-trends.html',
  335. 'Next Big Future' => '/news/next-big-future.html',
  336. 'Apple Insider' => '/news/apple-insider.html',
  337. 'Geek' => '/news/geek.html',
  338. 'BBC Technology' => '/news/bbc-technology.html',
  339. 'Bit-Tech' => '/news/bit-tech.html',
  340. 'Packet Storm Sec' => '/news/packet-storm-sec.html',
  341. 'Design' => '/news/design.html',
  342. 'High Scalability' => '/news/high-scalability.html',
  343. 'Smashing Mag' => '/news/smashing-mag.html',
  344. 'The Tech Block' => '/news/the-tech-block.html',
  345. 'A VC' => '/news/a-vc.html',
  346. 'Tech in Asia' => '/news/tech-in-asia.html',
  347. 'ReadWriteWeb' => '/news/readwriteweb.html',
  348. 'PC Mag' => '/news/pc-mag.html',
  349. 'Continuations' => '/news/continuations.html',
  350. 'Copyblogger' => '/news/copyblogger.html',
  351. 'Cult of Mac' => '/news/cult-of-mac.html',
  352. 'BetaBeat' => '/news/betabeat.html',
  353. 'MedGadget' => '/news/medgadget.html',
  354. 'SecuriTeam' => '/news/securiteam.html',
  355. 'Venture Beat' => '/news/venture-beat.html',
  356. ),
  357. 'Trend' => array(
  358. 'Trend Hunter' => '/news/trend-hunter.html',
  359. 'ApartmentT' => '/news/apartmentt.html',
  360. 'GQ' => '/news/gq.html',
  361. 'Digital Trends' => '/news/digital-trends.html',
  362. 'Cool Hunting' => '/news/cool-hunting.html',
  363. 'FastCoDesign' => '/news/fastcodesign.html',
  364. 'TC Startups' => '/news/tc-startups.html',
  365. 'Killer Startups' => '/news/killer-startups.html',
  366. 'DigiInfo' => '/news/digiinfo.html',
  367. 'New Startups' => '/news/new-startups.html',
  368. 'DigiTrends' => '/news/digitrends.html',
  369. ),
  370. 'Watches' => array(
  371. 'Hodinkee' => '/news/hodinkee.html',
  372. 'Quill and Pad' => '/news/quill-and-pad.html',
  373. 'Monochrome' => '/news/monochrome.html',
  374. 'Deployant' => '/news/deployant.html',
  375. 'Watches by SJX' => '/news/watches-by-sjx.html',
  376. 'Fratello Watches' => '/news/fratello-watches.html',
  377. 'A Blog to Watch' => '/news/a-blog-to-watch.html',
  378. 'Wound for Life' => '/news/wound-for-life.html',
  379. 'Watch Paper' => '/news/watch-paper.html',
  380. 'Watch Report' => '/news/watch-report.html',
  381. 'Perpetuelle' => '/news/perpetuelle.html',
  382. ),
  383. 'Youtube' => array(
  384. 'LinusTechTips' => '/news/linustechtips.html',
  385. 'MetalJesusRocks' => '/news/metaljesusrocks.html',
  386. 'TotalBiscuit' => '/news/totalbiscuit.html',
  387. 'DexBonus' => '/news/dexbonus.html',
  388. 'Lon Siedman' => '/news/lon-siedman.html',
  389. 'MKBHD' => '/news/mkbhd.html',
  390. 'Terry A Davis' => '/news/terry-a-davis.html',
  391. 'HappyConsole' => '/news/happyconsole.html',
  392. 'Austin Evans' => '/news/austin-evans.html',
  393. 'NCIX' => '/news/ncix.html',
  394. ),
  395. )
  396. ),
  397. ),
  398. self::CONTEXT_CUSTOM => array(
  399. 'config' => array(
  400. 'name' => 'Configuration',
  401. 'type' => 'text',
  402. 'required' => true,
  403. 'title' => 'Enter feed numbers from Skimfeed!',
  404. 'exampleValue' => '5,8,2,l,p,9,23'
  405. )
  406. ),
  407. 'global' => array(
  408. 'limit' => array(
  409. 'name' => 'Limit',
  410. 'type' => 'number',
  411. 'title' => 'Limits the number of returned items in the feed',
  412. 'exampleValue' => 10
  413. )
  414. )
  415. );
  416. public function getURI() {
  417. switch($this->queriedContext) {
  418. case self::CONTEXT_NEWS_BOX:
  419. $channel = $this->getInput('box_channel');
  420. if($channel) {
  421. return static::URI . $channel;
  422. }
  423. break;
  424. case self::CONTEXT_HOT_TOPICS:
  425. return static::URI;
  426. case self::CONTEXT_TECH_NEWS:
  427. $channel = $this->getInput('tech_channel');
  428. if($channel) {
  429. return static::URI . $channel;
  430. }
  431. break;
  432. case self::CONTEXT_CUSTOM:
  433. $config = $this->getInput('config');
  434. return static::URI . '/custom.php?f=' . urlencode($config);
  435. }
  436. return parent::getURI();
  437. }
  438. public function getName() {
  439. switch($this->queriedContext) {
  440. case self::CONTEXT_NEWS_BOX:
  441. $channel = $this->getInput('box_channel');
  442. $title = array_search(
  443. $channel,
  444. static::PARAMETERS[self::CONTEXT_NEWS_BOX]['box_channel']['values']
  445. );
  446. return $title . ' - ' . static::NAME;
  447. case self::CONTEXT_HOT_TOPICS:
  448. return 'Hot topics - ' . static::NAME;
  449. case self::CONTEXT_TECH_NEWS:
  450. $channel = $this->getInput('tech_channel');
  451. $titles = array();
  452. foreach(static::PARAMETERS[self::CONTEXT_TECH_NEWS]['tech_channel']['values'] as $ch) {
  453. $titles = array_merge($titles, $ch);
  454. }
  455. $title = array_search($channel, $titles);
  456. return $title . ' - ' . static::NAME;
  457. case self::CONTEXT_CUSTOM:
  458. return 'Custom - ' . static::NAME;
  459. }
  460. return parent::getName();
  461. }
  462. public function collectData() {
  463. // enable to export parameter lists
  464. // $this->exportBoxChannels(); die;
  465. // $this->exportTechChannels(); die;
  466. $html = getSimpleHTMLDOM($this->getURI())
  467. or returnServerError('Request to ' . $this->getURI() . ' failed!');
  468. defaultLinkTo($html, static::URI);
  469. switch($this->queriedContext) {
  470. case self::CONTEXT_NEWS_BOX:
  471. $author = array_search(
  472. $this->getInput('box_channel'),
  473. static::PARAMETERS[self::CONTEXT_NEWS_BOX]['box_channel']['values']
  474. );
  475. $author = '<a href="'
  476. . $this->getURI()
  477. . '">'
  478. . $author
  479. . '</a>';
  480. $this->extractFeed($html, $author);
  481. break;
  482. case self::CONTEXT_HOT_TOPICS:
  483. $this->extractHotTopics($html);
  484. break;
  485. case self::CONTEXT_TECH_NEWS:
  486. $authors = array();
  487. foreach(static::PARAMETERS[self::CONTEXT_TECH_NEWS]['tech_channel']['values'] as $ch) {
  488. $authors = array_merge($authors, $ch);
  489. }
  490. $author = '<a href="'
  491. . $this->getURI()
  492. . '">'
  493. . array_search($this->getInput('tech_channel'), $authors)
  494. . '</a>';
  495. $this->extractFeed($html, $author);
  496. break;
  497. case self::CONTEXT_CUSTOM:
  498. $this->extractCustomFeed($html);
  499. break;
  500. }
  501. }
  502. private function extractFeed($html, $author) {
  503. $articles = $html->find('li')
  504. or returnServerError('Could not find articles!');
  505. if(count($articles) === 1
  506. && stristr($articles[0]->plaintext, 'Nothing new in the last 48 hours')) {
  507. return; // Nothing to show
  508. }
  509. $limit = $this->getInput('limit') ?: -1;
  510. foreach($articles as $article) {
  511. $anchor = $article->find('a', 0)
  512. or returnServerError('Could not find anchor!');
  513. $item = array();
  514. $item['uri'] = $this->getTarget($anchor);
  515. $item['title'] = trim($anchor->plaintext);
  516. // The timestamp is encoded as relative time (max. the last 48 hours)
  517. // like this: "- 7 hours". It should always be at the end of the article:
  518. $age = substr($article->plaintext, strrpos($article->plaintext, '-'));
  519. $item['timestamp'] = strtotime($age);
  520. $item['author'] = $author;
  521. $this->items[] = $item;
  522. if($limit > 0 && count($this->items) >= $limit) {
  523. return;
  524. }
  525. }
  526. }
  527. private function extractHotTopics($html) {
  528. $topics = $html->find('#popbox ul li')
  529. or returnServerError('Could not find topics!');
  530. $limit = $this->getInput('limit') ?: -1;
  531. foreach($topics as $topic) {
  532. $anchor = $topic->find('a', 0)
  533. or returnServerError('Could not find anchor!');
  534. $item = array();
  535. $item['uri'] = $this->getTarget($anchor);
  536. $item['title'] = $anchor->title;
  537. $this->items[] = $item;
  538. if($limit > 0 && count($this->items) >= $limit) {
  539. return;
  540. }
  541. }
  542. }
  543. private function extractCustomFeed($html) {
  544. $boxes = $html->find('#boxx .boxes')
  545. or returnServerError('Could not find boxes!');
  546. foreach($boxes as $box) {
  547. $anchor = $box->find('span.boxtitles a', 0)
  548. or returnServerError('Could not find box anchor!');
  549. $author = '<a href="' . $anchor->href . '">' . trim($anchor->plaintext) . '</a>';
  550. $uri = $anchor->href;
  551. $box_html = getSimpleHTMLDOM($uri)
  552. or returnServerError('Could not load custom feed!');
  553. $this->extractFeed($box_html, $author);
  554. }
  555. }
  556. private function getTarget($anchor) {
  557. // Anchors are linked to Skimfeed, luckily the target URI is encoded
  558. // in that URI via '&u=<URI>':
  559. $query = parse_url($anchor->href, PHP_URL_QUERY);
  560. foreach(explode('&', $query) as $parameter) {
  561. list($key, $value) = explode('=', $parameter);
  562. if($key !== 'u') {
  563. continue;
  564. }
  565. return urldecode($value);
  566. }
  567. }
  568. /**
  569. * dev-mode!
  570. * Requires '&format=Html'
  571. *
  572. * Returns the 'box' array from the source site
  573. */
  574. private function exportBoxChannels() {
  575. $html = getSimpleHTMLDOMCached(static::URI)
  576. or returnServerError('No contents received from Skimfeed!');
  577. if(!$this->isCompatible($html)) {
  578. returnServerError('Skimfeed version is not compatible!');
  579. }
  580. $boxes = $html->find('#boxx .boxes')
  581. or returnServerError('Could not find boxes!');
  582. // begin of 'channel' list
  583. $message = <<<EOD
  584. 'box_channel' => array(
  585. 'name' => 'Channel',
  586. 'type' => 'list',
  587. 'required' => true,
  588. 'title' => 'Select your channel',
  589. 'values' => array(
  590. EOD;
  591. foreach($boxes as $box) {
  592. $anchor = $box->find('span.boxtitles a', 0)
  593. or returnServerError('Could not find box anchor!');
  594. $title = trim($anchor->plaintext);
  595. $uri = $anchor->href;
  596. // add value
  597. $message .= "\t\t'{$title}' => '{$uri}', \n";
  598. }
  599. // end of 'box' list
  600. $message .= <<<EOD
  601. )
  602. ),
  603. EOD;
  604. echo <<<EOD
  605. <!DOCTYPE html>
  606. <html>
  607. <body>
  608. <code style="white-space: pre-wrap;">{$message}</code>
  609. </body>
  610. </html>
  611. EOD;
  612. }
  613. /**
  614. * dev-mode!
  615. * Requires '&format=Html'
  616. *
  617. * Returns the 'techs' array from the source site
  618. */
  619. private function exportTechChannels() {
  620. $html = getSimpleHTMLDOMCached(static::URI)
  621. or returnServerError('No contents received from Skimfeed!');
  622. if(!$this->isCompatible($html)) {
  623. returnServerError('Skimfeed version is not compatible!');
  624. }
  625. $channels = $html->find('#menubar a')
  626. or returnServerError('Could not find channels!');
  627. // begin of 'tech_channel' list
  628. $message = <<<EOD
  629. 'tech_channel' => array(
  630. 'name' => 'Tech channel',
  631. 'type' => 'list',
  632. 'required' => true,
  633. 'title' => 'Select your tech channel',
  634. 'values' => array(
  635. EOD;
  636. foreach($channels as $channel) {
  637. if($channel->href === '#'
  638. || $channel->class === 'homelink'
  639. || $channel->plaintext === 'Twitter'
  640. || $channel->plaintext === 'Weather'
  641. || $channel->plaintext === '+Custom') {
  642. continue;
  643. }
  644. $title = trim($channel->plaintext);
  645. $uri = '/' . $channel->href;
  646. $message .= "\t\t'{$title}' => array(\n";
  647. $channel_html = getSimpleHTMLDOMCached(static::URI . $uri)
  648. or returnServerError('Could not load tech channel ' . $channel->plaintext . '!');
  649. $boxes = $channel_html->find('#boxx .boxes')
  650. or returnServerError('Could not find boxes!');
  651. foreach($boxes as $box) {
  652. $anchor = $box->find('span.boxtitles a', 0)
  653. or returnServerError('Could not find box anchor!');
  654. $boxtitle = trim($anchor->plaintext);
  655. $boxuri = $anchor->href;
  656. $message .= "\t\t\t'{$boxtitle}' => '{$boxuri}', \n";
  657. }
  658. $message .= "\t\t),\n";
  659. }
  660. // end of 'box' list
  661. $message .= <<<EOD
  662. )
  663. ),
  664. EOD;
  665. echo <<<EOD
  666. <!DOCTYPE html>
  667. <html>
  668. <body>
  669. <code style="white-space: pre-wrap;">{$message}</code>
  670. </body>
  671. </html>
  672. EOD;
  673. }
  674. /**
  675. * Checks if the reported skimfeed version is compatible
  676. */
  677. private function isCompatible($html) {
  678. $title = $html->find('title', 0);
  679. if(!$title) {
  680. return false;
  681. }
  682. if($title->plaintext === 'Skimfeed V5.5 - Tech News') {
  683. return true;
  684. }
  685. return false;
  686. }
  687. }