Add basic news reader functionality
parent
7d30251cd6
commit
ac1e937dfd
@ -1,4 +1,5 @@
|
|||||||
vendor
|
vendor
|
||||||
settings.php
|
settings.php
|
||||||
nbproject/private
|
nbproject/private
|
||||||
*.sync-conflict*
|
*.sync-conflict*
|
||||||
|
/database.mwb.bak
|
@ -1,3 +1,6 @@
|
|||||||
[submodule "static/css/material-color"]
|
[submodule "static/css/material-color"]
|
||||||
path = static/css/material-color
|
path = static/css/material-color
|
||||||
url = https://source.netsyms.com/Netsyms/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
|
||||||
|
Binary file not shown.
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"business": "Business",
|
||||||
|
"entertainment": "Entertainment",
|
||||||
|
"general": "General",
|
||||||
|
"health": "Health",
|
||||||
|
"science": "Science",
|
||||||
|
"sports": "Sports",
|
||||||
|
"technology": "Technology"
|
||||||
|
}
|
@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,54 @@
|
|||||||
<?php
|
<?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
|
* 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;
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<h1>Hello World</h1>
|
|
||||||
|
<?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
|
||||||
|
}
|
||||||
|
?>
|
@ -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>
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c742bfaefc8908170825227a7385f20e0ba5da37
|
@ -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…
Reference in New Issue