Add basic news reader functionality
This commit is contained in:
parent
7d30251cd6
commit
ac1e937dfd
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ vendor
|
||||
settings.php
|
||||
nbproject/private
|
||||
*.sync-conflict*
|
||||
/database.mwb.bak
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "static/css/material-color"]
|
||||
path = static/css/material-color
|
||||
url = https://source.netsyms.com/Netsyms/Material-Color
|
||||
[submodule "static/Shuffle"]
|
||||
path = static/Shuffle
|
||||
url = https://source.netsyms.com/Mirrors/Vestride_Shuffle.git
|
||||
|
@ -35,4 +35,8 @@ switch ($VARS['action']) {
|
||||
session_destroy();
|
||||
header('Location: index.php?logout=1');
|
||||
die("Logged out.");
|
||||
case "thumbnail":
|
||||
header("Content-Type: image/jpeg");
|
||||
// TODO
|
||||
break;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "netsyms/business-app-template",
|
||||
"description": "Template for a webapp integrated with an AccountHub server.",
|
||||
"name": "netsyms/todaystream",
|
||||
"description": "News and weather app.",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"catfan/medoo": "^1.5",
|
||||
|
77
composer.lock
generated
77
composer.lock
generated
@ -4,21 +4,20 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "5c7439c6e041764f2f6b0270a95ab3ae",
|
||||
"content-hash": "e4e700119f47d2f68b0ed82abaf8c5c6",
|
||||
"content-hash": "5642e5d15688c9510f4b0d1b4ceed67e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "catfan/medoo",
|
||||
"version": "v1.5.7",
|
||||
"version": "v1.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/catfan/Medoo.git",
|
||||
"reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf"
|
||||
"reference": "53a02b300d673f716cb06bf0e24fd774ec53939f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/catfan/Medoo/zipball/8d90cba0e8ff176028847527d0ea76fe41a06ecf",
|
||||
"reference": "8d90cba0e8ff176028847527d0ea76fe41a06ecf",
|
||||
"url": "https://api.github.com/repos/catfan/Medoo/zipball/53a02b300d673f716cb06bf0e24fd774ec53939f",
|
||||
"reference": "53a02b300d673f716cb06bf0e24fd774ec53939f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -64,7 +63,7 @@
|
||||
"sql",
|
||||
"sqlite"
|
||||
],
|
||||
"time": "2018-06-14 18:59:08"
|
||||
"time": "2018-12-08T20:24:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
@ -129,7 +128,7 @@
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2018-04-22 15:46:56"
|
||||
"time": "2018-04-22T15:46:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
@ -180,36 +179,37 @@
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"time": "2016-12-20 10:07:11"
|
||||
"time": "2016-12-20T10:07:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
|
||||
"reference": "9f83dded91781a01c63574e387eaa769be769115"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
|
||||
"reference": "9f83dded91781a01c63574e387eaa769be769115",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"psr/http-message": "~1.0"
|
||||
"psr/http-message": "~1.0",
|
||||
"ralouphie/getallheaders": "^2.0.5"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -239,13 +239,14 @@
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2017-03-20 17:10:46"
|
||||
"time": "2018-12-04T20:46:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
@ -295,7 +296,47 @@
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2016-08-06 14:39:51"
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "2.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
|
||||
"reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~3.7.0",
|
||||
"satooshi/php-coveralls": ">=1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"time": "2016-02-11T07:05:27+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
BIN
database.mwb
Normal file
BIN
database.mwb
Normal file
Binary file not shown.
9
langs/en/categories.json
Normal file
9
langs/en/categories.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"business": "Business",
|
||||
"entertainment": "Entertainment",
|
||||
"general": "General",
|
||||
"health": "Health",
|
||||
"science": "Science",
|
||||
"sports": "Sports",
|
||||
"technology": "Technology"
|
||||
}
|
85
lib/ApiFetcher.lib.php
Normal file
85
lib/ApiFetcher.lib.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class ApiFetcher {
|
||||
|
||||
/**
|
||||
* Gets a URL. Caches and returns the result, but if the cache has a recent copy
|
||||
* return that instead of making the request.
|
||||
*
|
||||
* Basically, the goal is to stay within the free tier of paid APIs :)
|
||||
*
|
||||
* @global Medoo $database
|
||||
* @param string $url
|
||||
* @param array $params key=>value array of URL parameters
|
||||
* @param array $extraParams key=>value array of URL parameters that will be ignored when checking the cache
|
||||
* @param string $expires When the fetched resource should expire from the cache. A string parsable by strtotime()
|
||||
* @return string The content of the requested URL.
|
||||
*/
|
||||
static function get(string $url, array $params = [], array $extraParams = [], string $expires = "+15 minutes"): string {
|
||||
global $database;
|
||||
|
||||
// Delete stale records
|
||||
$database->delete("requestcache", ["expires[<=]" => date("Y-m-d H:i:s")]);
|
||||
|
||||
// Make sure the params are in the same order every time
|
||||
ksort($params);
|
||||
|
||||
$urlparams = [];
|
||||
foreach ($params as $k => $v) {
|
||||
$urlparams[] = urlencode($k) . "=" . urlencode($v);
|
||||
}
|
||||
|
||||
// Make the URL that will be cached
|
||||
$cacheurl = $url . "?" . implode("&", $urlparams);
|
||||
|
||||
foreach ($extraParams as $k => $v) {
|
||||
$urlparams[] = urlencode($k) . "=" . urlencode($v);
|
||||
}
|
||||
|
||||
if ($database->has("requestcache", ["AND" => ["url" => $cacheurl, "expires[>]" => date("Y-m-d H:i:s")]])) {
|
||||
return $database->get("requestcache", "content", ["AND" => ["url" => $cacheurl, "expires[>]" => date("Y-m-d H:i:s")]]);
|
||||
}
|
||||
|
||||
// Make the actual URL that will be requested
|
||||
$requesturl = $url . "?" . implode("&", $urlparams);
|
||||
|
||||
$content = file_get_contents($requesturl);
|
||||
|
||||
// Only insert into db if it didn't fail horribly
|
||||
if ($content !== FALSE) {
|
||||
$database->insert("requestcache", ["url" => $cacheurl, "expires" => date("Y-m-d H:i:s", strtotime($expires)), "content" => $content]);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the given URL from the cache.
|
||||
*
|
||||
* @global Medoo $database
|
||||
* @param string $url
|
||||
* @param array $params key=>value array of URL parameters
|
||||
*/
|
||||
static function removeFromCache(string $url = "", array $params = []) {
|
||||
global $database;
|
||||
|
||||
// Make sure the params are in the same order every time
|
||||
ksort($params);
|
||||
|
||||
$urlparams = [];
|
||||
foreach ($params as $k => $v) {
|
||||
$urlparams[] = urlencode($k) . "=" . urlencode($v);
|
||||
}
|
||||
|
||||
$cacheurl = $url . "?" . implode("&", $urlparams);
|
||||
|
||||
$database->delete("requestcache", ["url" => $cacheurl]);
|
||||
}
|
||||
|
||||
}
|
37
lib/News.lib.php
Normal file
37
lib/News.lib.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Load news items from multiple sources.
|
||||
*/
|
||||
class News {
|
||||
|
||||
static $items = [];
|
||||
|
||||
/**
|
||||
* Load news items from each source.
|
||||
* @param array $sources
|
||||
*/
|
||||
static function load(array $sources) {
|
||||
foreach ($sources as $s) {
|
||||
$class = "NewsSource_$s";
|
||||
$source = new $class();
|
||||
$source->loadItems();
|
||||
News::$items = array_merge(News::$items, $source->getItems());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all news items from all sources loaded.
|
||||
* @return array
|
||||
*/
|
||||
static function getItems() {
|
||||
return News::$items;
|
||||
}
|
||||
|
||||
}
|
123
lib/NewsCategory.lib.php
Normal file
123
lib/NewsCategory.lib.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class NewsCategory {
|
||||
|
||||
private $category;
|
||||
|
||||
const NONE_ALL = -1;
|
||||
const BUSINESS = 1;
|
||||
const ENTERTAINMENT = 2;
|
||||
const GENERAL = 4;
|
||||
const HEALTH = 8;
|
||||
const SCIENCE = 16;
|
||||
const SPORTS = 32;
|
||||
const TECHNOLOGY = 64;
|
||||
|
||||
const CATEGORIES = [
|
||||
self::BUSINESS => "Business",
|
||||
self::ENTERTAINMENT => "Entertainment",
|
||||
self::GENERAL => "General",
|
||||
self::HEALTH => "Health",
|
||||
self::SCIENCE => "Science",
|
||||
self::SPORTS => "Sports",
|
||||
self::TECHNOLOGY => "Technology"
|
||||
];
|
||||
|
||||
public function __construct(int $category) {
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category as an int corresponding to one of the constants.
|
||||
* @return int
|
||||
*/
|
||||
public function get(): int {
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string representation of the category.
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string {
|
||||
switch ($this->category) {
|
||||
case self::BUSINESS:
|
||||
return "business";
|
||||
case self::ENTERTAINMENT:
|
||||
return "entertainment";
|
||||
case self::GENERAL:
|
||||
return "general";
|
||||
case self::HEALTH:
|
||||
return "health";
|
||||
case self::SCIENCE:
|
||||
return "science";
|
||||
case self::SPORTS:
|
||||
return "sports";
|
||||
case self::TECHNOLOGY:
|
||||
return "technology";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromString(string $category): NewsCategory {
|
||||
$cat = self::NONE_ALL;
|
||||
switch (strtolower($category)) {
|
||||
case "business":
|
||||
$cat = self::BUSINESS;
|
||||
break;
|
||||
case "entertainment":
|
||||
$cat = self::ENTERTAINMENT;
|
||||
break;
|
||||
case "general":
|
||||
$cat = self::GENERAL;
|
||||
break;
|
||||
case "health":
|
||||
$cat = self::HEALTH;
|
||||
break;
|
||||
case "science":
|
||||
$cat = self::SCIENCE;
|
||||
break;
|
||||
case "sports":
|
||||
$cat = self::SPORTS;
|
||||
break;
|
||||
case "technology":
|
||||
case "tech":
|
||||
$cat = self::TECHNOLOGY;
|
||||
break;
|
||||
}
|
||||
return new NewsCategory($cat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a suitable FontAwesome 5 icon for the category.
|
||||
* @return string CSS classes, such as "fas fa-icon".
|
||||
*/
|
||||
public function getIcon(): string {
|
||||
switch ($this->category) {
|
||||
case self::BUSINESS:
|
||||
return "fas fa-briefcase";
|
||||
case self::ENTERTAINMENT:
|
||||
return "fas fa-tv";
|
||||
case self::GENERAL:
|
||||
return "fas fa-info-circle";
|
||||
case self::HEALTH:
|
||||
return "fas fa-heartbeat";
|
||||
case self::SCIENCE:
|
||||
return "fas fa-atom";
|
||||
case self::SPORTS:
|
||||
return "fas fa-futbol";
|
||||
case self::TECHNOLOGY:
|
||||
return "fas fa-laptop";
|
||||
default:
|
||||
return "fas fa-newspaper";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
61
lib/NewsItem.lib.php
Normal file
61
lib/NewsItem.lib.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class NewsItem {
|
||||
|
||||
private $img = "";
|
||||
private $headline = "";
|
||||
private $source = "";
|
||||
private $via = "";
|
||||
private $url = "";
|
||||
private $timestamp = 0;
|
||||
private $category;
|
||||
|
||||
function __construct(string $headline = "", string $img = "", string $url = "", string $source = "", string $via = "", int $timestamp = 0, $category = null) {
|
||||
$this->headline = $headline;
|
||||
$this->img = $img;
|
||||
$this->url = $url;
|
||||
$this->source = $source;
|
||||
$this->via = $via;
|
||||
$this->timestamp = $timestamp;
|
||||
if (is_null($category)) {
|
||||
$this->category = new NewsCategory(NewsCategory::GENERAL);
|
||||
} else {
|
||||
$this->category = $category;
|
||||
}
|
||||
}
|
||||
|
||||
function getImage(): string {
|
||||
return $this->img;
|
||||
}
|
||||
|
||||
function getURL(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
function getHeadline(): string {
|
||||
return $this->headline;
|
||||
}
|
||||
|
||||
function getSource(): string {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
function getVia(): string {
|
||||
return $this->via;
|
||||
}
|
||||
|
||||
function getTimestamp(): int {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
function getCategory(): \NewsCategory {
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
}
|
27
lib/NewsSource.lib.php
Normal file
27
lib/NewsSource.lib.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for news sources.
|
||||
*/
|
||||
class NewsSource {
|
||||
|
||||
private $items = [];
|
||||
|
||||
function getItems(): array {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch news items from this source.
|
||||
*/
|
||||
function loadItems() {
|
||||
// Do nothing, because this is a dummy source
|
||||
}
|
||||
|
||||
}
|
66
lib/NewsSource_NewsAPI.lib.php
Normal file
66
lib/NewsSource_NewsAPI.lib.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class NewsSource_NewsAPI extends NewsSource {
|
||||
|
||||
private $items = [];
|
||||
|
||||
function getItems(): array {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
function loadItems() {
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::BUSINESS)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::ENTERTAINMENT)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::GENERAL)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::HEALTH)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::SCIENCE)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::SPORTS)));
|
||||
$this->loadHeadlines((new NewsCategory(NewsCategory::TECHNOLOGY)));
|
||||
}
|
||||
|
||||
private function loadHeadlines($category = null, string $country = "us", $apikey = null) {
|
||||
global $SETTINGS;
|
||||
if (is_null($category)) {
|
||||
$category = new NewsCategory(NewsCategory::GENERAL);
|
||||
}
|
||||
if (is_null($apikey)) {
|
||||
$apikey = $SETTINGS['apikeys']['newsapi.org'];
|
||||
}
|
||||
$url = "https://newsapi.org/v2/top-headlines";
|
||||
$params = [];
|
||||
if (!empty($country)) {
|
||||
$params['country'] = $country;
|
||||
}
|
||||
if (!empty($category->toString())) {
|
||||
$params['category'] = $category->toString();
|
||||
}
|
||||
$items = [];
|
||||
$json = ApiFetcher::get($url, $params, ["apiKey" => $apikey]);
|
||||
$data = json_decode($json, TRUE);
|
||||
if ($data['status'] != "ok") {
|
||||
return [];
|
||||
}
|
||||
$articles = $data['articles'];
|
||||
foreach ($articles as $n) {
|
||||
$title = $n['title'];
|
||||
$image = $n['urlToImage'];
|
||||
if (is_null($image)) {
|
||||
continue;
|
||||
}
|
||||
$url = $n['url'];
|
||||
$source = $n['source']['name'];
|
||||
$timestamp = strtotime($n['publishedAt']);
|
||||
|
||||
$items[] = new NewsItem($title, $image, $url, $source, "NewsAPI.org", $timestamp, $category);
|
||||
}
|
||||
|
||||
$this->items = array_merge($this->items, $items);
|
||||
}
|
||||
|
||||
}
|
72
lib/NewsSource_Reddit.lib.php
Normal file
72
lib/NewsSource_Reddit.lib.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class NewsSource_Reddit extends NewsSource {
|
||||
|
||||
private $items = [];
|
||||
|
||||
function getItems(): array {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
function loadItems() {
|
||||
$this->loadSubreddit("news+worldnews+UpliftingNews", new NewsCategory(NewsCategory::GENERAL));
|
||||
//$this->loadSubreddit("technology", new NewsCategory(NewsCategory::TECHNOLOGY));
|
||||
}
|
||||
|
||||
private function loadSubreddit(string $subreddit, NewsCategory $category) {
|
||||
$items = [];
|
||||
$json = ApiFetcher::get("https://www.reddit.com/r/$subreddit.json", ["limit" => "50"]);
|
||||
$news = json_decode($json, TRUE)['data']['children'];
|
||||
foreach ($news as $d) {
|
||||
$n = $d['data'];
|
||||
|
||||
// Ignore non-linky or NSFW posts
|
||||
if ($n['is_self']) {
|
||||
continue;
|
||||
}
|
||||
if ($n['is_video']) {
|
||||
continue;
|
||||
}
|
||||
if ($n['over_18']) {
|
||||
continue;
|
||||
}
|
||||
if (empty($n['url'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Thumbnail image
|
||||
$image = "";
|
||||
if (isset($n['thumbnail']) && $n['thumbnail'] != "default") {
|
||||
$image = $n['thumbnail'];
|
||||
}
|
||||
if (isset($n['preview']['images'][0]['resolutions'])) {
|
||||
if (isset($n['preview']['images'][0]['resolutions'][2]['url'])) {
|
||||
$image = $n['preview']['images'][0]['resolutions'][2]['url'];
|
||||
} else if (isset($n['preview']['images'][0]['resolutions'][1]['url'])) {
|
||||
$image = $n['preview']['images'][0]['resolutions'][1]['url'];
|
||||
}
|
||||
}
|
||||
|
||||
$source = "reddit.com";
|
||||
if (!empty($n['domain'])) {
|
||||
$source = $n['domain'];
|
||||
}
|
||||
|
||||
$timestamp = time();
|
||||
if (!empty($n['created'])) {
|
||||
$timestamp = $n['created'];
|
||||
}
|
||||
|
||||
$items[] = new NewsItem($n['title'], $image, $n['url'], $source, "reddit", $timestamp, $category);
|
||||
}
|
||||
|
||||
$this->items = array_merge($this->items, $items);
|
||||
}
|
||||
|
||||
}
|
60
lib/Thumbnail.lib.php
Normal file
60
lib/Thumbnail.lib.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Thumbnail {
|
||||
|
||||
/**
|
||||
* Fetches an image URL, resizes it, and returns JPEG thumbnail data.
|
||||
* Based on https://stackoverflow.com/a/29024968
|
||||
* @param string $url
|
||||
* @param int $width
|
||||
* @param int,bool $height
|
||||
* @return type
|
||||
*/
|
||||
static function getThumbnailFromUrl(string $url, int $width = 150, $height = true) {
|
||||
|
||||
// download and create gd image
|
||||
$image = imagecreatefromstring(file_get_contents($url));
|
||||
|
||||
// calculate resized ratio
|
||||
// Note: if $height is set to TRUE then we automatically calculate the height based on the ratio
|
||||
$height = $height === true ? (imagesy($image) * $width / imagesx($image)) : $height;
|
||||
|
||||
// create image
|
||||
$output = imagecreatetruecolor($width, $height);
|
||||
imagecopyresampled($output, $image, 0, 0, 0, 0, $width, $height, ImageSX($image), ImageSY($image));
|
||||
|
||||
ob_end_flush();
|
||||
ob_start();
|
||||
imagejpeg($output, null, 75);
|
||||
$imagedata = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// return resized image
|
||||
return $imagedata; // if you need to use it
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes some data to base64.
|
||||
* @param type $img
|
||||
* @return string
|
||||
*/
|
||||
static function imgToBase64($img): string {
|
||||
return base64_encode($img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base64 data: URI for a jpeg image
|
||||
* @param type $img
|
||||
* @return string
|
||||
*/
|
||||
static function jpegToBase64URI($img): string {
|
||||
return "data:image/jpeg;base64," . self::imgToBase64($img);
|
||||
}
|
||||
|
||||
}
|
11
lib/Weather.lib.php
Normal file
11
lib/Weather.lib.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Weather {
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<type>org.netbeans.modules.php.project</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/php-project/1">
|
||||
<name>BusinessAppTemplate</name>
|
||||
<name>TodayStream</name>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
||||
|
25
pages.php
25
pages.php
@ -7,19 +7,24 @@
|
||||
// List of pages and metadata
|
||||
define("PAGES", [
|
||||
"home" => [
|
||||
"title" => "Home",
|
||||
"title" => "Overview",
|
||||
"navbar" => true,
|
||||
"icon" => "fas fa-home"
|
||||
"icon" => "fas fa-home",
|
||||
"scripts" => [
|
||||
"static/Shuffle/dist/shuffle.min.js",
|
||||
"static/js/home.js"
|
||||
]
|
||||
],
|
||||
"news" => [
|
||||
"title" => "News",
|
||||
"navbar" => true,
|
||||
"icon" => "fas fa-newspaper",
|
||||
"scripts" => [
|
||||
"static/Shuffle/dist/shuffle.min.js",
|
||||
"static/js/news.js"
|
||||
]
|
||||
],
|
||||
"404" => [
|
||||
"title" => "404 error"
|
||||
],
|
||||
"form" => [
|
||||
"title" => "Form",
|
||||
"navbar" => true,
|
||||
"icon" => "fas fa-file-alt",
|
||||
"scripts" => [
|
||||
"static/js/form.js"
|
||||
]
|
||||
]
|
||||
]);
|
@ -1,6 +1,54 @@
|
||||
<?php
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
News::load($SETTINGS["sources"]["news"]);
|
||||
|
||||
$newsitems = News::getItems();
|
||||
|
||||
// Sort by category
|
||||
$itemsbycategory = [];
|
||||
foreach ($newsitems as $item) {
|
||||
$itemsbycategory[$item->getCategory()->toString()][] = $item;
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
foreach ($itemsbycategory as $category => $items) {
|
||||
$cat = NewsCategory::fromString($category);
|
||||
?>
|
||||
<p class="lead">
|
||||
<i class="<?php echo $cat->getIcon(); ?> fa-fw"></i> <?php $Strings->get($cat->toString()); ?>
|
||||
</p>
|
||||
<div class="list-group mb-4">
|
||||
<?php
|
||||
shuffle($items);
|
||||
$count = 0;
|
||||
foreach ($items as $item) {
|
||||
if ($count >= 5) {
|
||||
break;
|
||||
}
|
||||
$count++;
|
||||
?>
|
||||
<div class="list-group-item d-flex justify-content-between">
|
||||
<a class="text-dark" href="<?php echo $item->getURL(); ?>" target="_BLANK">
|
||||
<h4>
|
||||
<?php echo htmlentities($item->getHeadline()); ?>
|
||||
</h4>
|
||||
<p class="text-muted"><?php echo htmlentities($item->getSource()); ?></p>
|
||||
</a>
|
||||
|
||||
<?php if (!empty($item->getImage())) { ?>
|
||||
<img src="<?php echo Thumbnail::jpegToBase64URI(Thumbnail::getThumbnailFromUrl($item->getImage(), 100)); ?>" alt="" class="img-fluid">
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h1>Hello World</h1>
|
41
pages/news.php
Normal file
41
pages/news.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
News::load($SETTINGS["sources"]["news"]);
|
||||
|
||||
$newsitems = News::getItems();
|
||||
?>
|
||||
|
||||
<div class="btn-toolbar mb-4" role="toolbar">
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<?php foreach (NewsCategory::CATEGORIES as $cat) { ?>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="newscategory" id="category-btn-<?php echo strtolower($cat); ?>" value="<?php echo strtolower($cat); ?>" autocomplete="off"> <?php $Strings->get($cat); ?>
|
||||
</label>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="news-grid">
|
||||
|
||||
<?php foreach ($newsitems as $item) { ?>
|
||||
<div class="col-12 col-sm-6 col-md-6 col-lg-4 px-1 m-0 grid__brick" data-groups='["<?php echo $item->getCategory()->toString(); ?>"]'>
|
||||
<div class="card mb-2">
|
||||
<?php if (!empty($item->getImage())) { ?>
|
||||
<img src="<?php echo $item->getImage(); ?>" class="card-img-top" alt="">
|
||||
<?php } ?>
|
||||
<a class="card-body text-dark" href="<?php echo $item->getURL(); ?>" target="_BLANK">
|
||||
<h4 class="card-title">
|
||||
<?php echo htmlentities($item->getHeadline()); ?>
|
||||
</h4>
|
||||
<p class="small"><?php echo htmlentities($item->getSource()); ?></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="col-1 sizer-element"></div>
|
||||
|
||||
</div>
|
@ -19,14 +19,14 @@ $SETTINGS = [
|
||||
// See http://medoo.in/api/new for info
|
||||
"database" => [
|
||||
"type" => "mysql",
|
||||
"name" => "app",
|
||||
"name" => "todaystream",
|
||||
"server" => "localhost",
|
||||
"user" => "app",
|
||||
"user" => "",
|
||||
"password" => "",
|
||||
"charset" => "utf8"
|
||||
],
|
||||
// Name of the app.
|
||||
"site_title" => "Web App Template",
|
||||
"site_title" => "TodayStream",
|
||||
// Settings for connecting to the AccountHub server.
|
||||
"accounthub" => [
|
||||
// URL for the API endpoint
|
||||
@ -36,6 +36,21 @@ $SETTINGS = [
|
||||
// API key
|
||||
"key" => "123"
|
||||
],
|
||||
// API keys for data sources
|
||||
"apikeys" => [
|
||||
"newsapi.org" => "",
|
||||
"darksky.net" => "",
|
||||
],
|
||||
// Which data sources to use
|
||||
"sources" => [
|
||||
"news" => [
|
||||
"NewsAPI",
|
||||
"Reddit"
|
||||
],
|
||||
"weather" => [
|
||||
"DarkSky"
|
||||
]
|
||||
],
|
||||
// List of required user permissions to access this app.
|
||||
"permissions" => [
|
||||
],
|
||||
@ -44,7 +59,7 @@ $SETTINGS = [
|
||||
// Language to use for localization. See langs folder to add a language.
|
||||
"language" => "en",
|
||||
// Shown in the footer of all the pages.
|
||||
"footer_text" => "",
|
||||
"footer_text" => "Data sources: <a href=\"https://newsapi.org\">NewsAPI.org</a>, <a href=\"https://reddit.com\">Reddit</a>, <a href=\"https://darksky.net\">Dark Sky</a>",
|
||||
// Also shown in the footer, but with "Copyright <current_year>" in front.
|
||||
"copyright" => "Netsyms Technologies",
|
||||
// Base URL for building links relative to the location of the app.
|
||||
|
1
static/Shuffle
Submodule
1
static/Shuffle
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c742bfaefc8908170825227a7385f20e0ba5da37
|
19
static/js/news.js
Normal file
19
static/js/news.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
window.shuffleInstance = new window.Shuffle(document.getElementById('news-grid'), {
|
||||
itemSelector: '.grid__brick',
|
||||
sizer: '.sizer-element'
|
||||
});
|
||||
|
||||
$("input[name=newscategory]").on("change", function () {
|
||||
window.shuffleInstance.filter($(this).val());
|
||||
$(this).button('toggle');
|
||||
});
|
||||
|
||||
setInterval(function () {
|
||||
window.shuffleInstance.layout();
|
||||
}, 500);
|
Loading…
x
Reference in New Issue
Block a user