<?php
namespace Empire\Core;
use Empire\Legacy\cgt;
use Empire\Legacy\db;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class News extends AbstractEntity implements Entity {
const PRIMARY_FIELD = "NEWS_ID";
const TABLE = "NEWS";
CONST NEWS_TYPE_INS = 1;
const NEWS_TYPE_DEV = 2;
public $title;
public $text;
public $blurb;
public $time;
public $newsType;
public $poster;
public function __construct() {
$this->type = cint_TYPE_NEWS;
}
function initialize() {
$this->id = $this->NEWS_ID;
$this->text = html_entity_decode($this->NEWS_TEXT);
$this->blurb = self::truncateHtml(self::stripImages($this->text), 500);
$this->title = $this->NEWS_TITLE;
$this->time = cgt::evaluateString($this->NEWS_STAMP);
$this->newsType = $this->NEWS_TYPE;
$this->poster = User::load([$this->MEMB_ID]);
}
function getPrimaryField() {
return self::PRIMARY_FIELD;
}
function getTable() {
return self::TABLE;
}
public static function getLatestNews($type = self::NEWS_TYPE_INS, $limit = 6) {
$query = db::prepare(sprintf("SELECT * FROM %s WHERE NEWS_TYPE = %d ORDER BY NEWS_STAMP DESC LIMIT %d ", self::TABLE, $type, $limit));
return db::fetchArrayOfObjects($query, self::class);
}
public static function getArchiveNews($page = 1, $type = self::NEWS_TYPE_INS, $term = "", $perPage = 10) {
$offset = ($page - 1) * $perPage;
if(strlen($term) > 0) {
$term = db::quoteEscape('%'.$term.'%');
$sql = sprintf("SELECT * FROM %s WHERE NEWS_TYPE = %d AND (NEWS_TEXT like %s OR NEWS_TITLE like %s) ORDER BY NEWS_STAMP DESC LIMIT %d OFFSET %d", self::TABLE, $type, $term, $term, $perPage, $offset);
} else {
$sql = sprintf("SELECT * FROM %s WHERE NEWS_TYPE = %d ORDER BY NEWS_STAMP DESC LIMIT %d OFFSET %d", self::TABLE, $type, $perPage, $offset);
}
$query = db::prepare($sql);
return db::fetchArrayOfObjects($query, self::class);
}
public static function getNewsCount($type = News::NEWS_TYPE_INS, $term = "") {
if(strlen($term) > 0) {
$term = db::quoteEscape('%'.$term.'%');
$sql = sprintf("SELECT COUNT(*) AS COUNT FROM %s WHERE NEWS_TYPE = %d AND (NEWS_TEXT like %s OR NEWS_TITLE like %s) ", self::TABLE, $type, $term, $term);
} else {
$sql = sprintf("SELECT COUNT(*) AS COUNT FROM %s WHERE NEWS_TYPE = %d", self::TABLE, $type);
}
$query = db::prepare($sql);
return db::fetchObject($query)->COUNT;
}
/**
* truncateHtml can truncate a string up to a number of characters while preserving whole words and HTML tags
*
* @param string $text String to truncate.
* @param integer $length Length of returned string, including ellipsis.
* @param string $ending Ending to be appended to the trimmed string.
* @param boolean $exact If false, $text will not be cut mid-word
* @param boolean $considerHtml If true, HTML tags would be handled correctly
*
* @return string Trimmed string.
*/
public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) {
if ($considerHtml) {
// if the plain text is shorter than the maximum length, return the whole text
if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
return $text;
}
// splits all html-tags to scanable lines
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
$total_length = strlen($ending);
$open_tags = array();
$truncate = '';
foreach ($lines as $line_matchings) {
// if there is any html-tag in this line, handle it and add it (uncounted) to the output
if (!empty($line_matchings[1])) {
// if it's an "empty element" with or without xhtml-conform closing slash
if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
// do nothing
// if tag is a closing tag
} else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
// delete tag from $open_tags list
$pos = array_search($tag_matchings[1], $open_tags);
if ($pos !== false) {
unset($open_tags[$pos]);
}
// if tag is an opening tag
} else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
// add tag to the beginning of $open_tags list
array_unshift($open_tags, strtolower($tag_matchings[1]));
}
// add html-tag to $truncate'd text
$truncate .= $line_matchings[1];
}
// calculate the length of the plain text part of the line; handle entities as one character
$content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
if ($total_length + $content_length > $length) {
// the number of characters which are left
$left = $length - $total_length;
$entities_length = 0;
// search for html entities
if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
// calculate the real length of all entities in the legal range
foreach ($entities[0] as $entity) {
if ($entity[1] + 1 - $entities_length <= $left) {
$left--;
$entities_length += strlen($entity[0]);
} else {
// no more characters left
break;
}
}
}
$truncate .= substr($line_matchings[2], 0, $left + $entities_length);
// maximum lenght is reached, so get off the loop
break;
} else {
$truncate .= $line_matchings[2];
$total_length += $content_length;
}
// if the maximum length is reached, get off the loop
if ($total_length >= $length) {
break;
}
}
} else {
if (strlen($text) <= $length) {
return $text;
} else {
$truncate = substr($text, 0, $length - strlen($ending));
}
}
// if the words shouldn't be cut in the middle...
if (!$exact) {
// ...search the last occurance of a space...
$spacepos = strrpos($truncate, ' ');
if (isset($spacepos)) {
// ...and cut the text in this position
$truncate = substr($truncate, 0, $spacepos);
}
}
// add the defined ending to the text
$truncate .= $ending;
if ($considerHtml) {
// close all unclosed html-tags
foreach ($open_tags as $tag) {
$truncate .= '</' . $tag . '>';
}
}
return $truncate;
}
public static function stripImages($text) {
return preg_replace("/<img[^>]+\>/i", "", $text);
}
public static function postNews($intUserID, $newsText, $newsType, $newsTitle) {
$newsText = htmlentities($newsText, ENT_QUOTES);
$newsTitle = htmlentities($newsTitle, ENT_QUOTES);
$now = time();
$result = Core::db()->prepare("INSERT INTO NEWS (`NEWS_TITLE`,`NEWS_TEXT`,`NEWS_STAMP`,`NEWS_TYPE`,`MEMB_ID`) VALUES ('$newsTitle','$newsText','$now','$newsType','$intUserID')");
$result->execute();
if ($result) {
Event::send($intUserID, 0, User::load([$intUserID])->getName() . " has posted the news article \"$newsTitle\".", cint_EVENTTYPE_NEWS);
}
// TODO: INB Posts
if ( Core::get('discord.postINB') == 'true') {
$hooktext = substr(strip_tags(htmlspecialchars_decode($newsTitle, ENT_QUOTES)),0 ,500 ) . "... Read the full article at https://swc-empire.com";
$hooktext = str_replace(' ', ' ', $hooktext);
$hooktext = str_replace('—', '-', $hooktext);
if ($_ENV['APP_ENV'] == "dev") {
self::postDiscordHook($hooktext);
}
}
}
public static function postDiscordHook($message) {
// for now, wrap this in a big if to avoid oopsies
if($_ENV['APP_ENV'] == "dev"){
$env = $_ENV['APP_ENV'];
$url = Core::get("discord.$env.inb.webhookUrl");
$av = Core::get("discord.$env.inb.avatarUrl");
$usr = Core::get("discord.$env.inb.username");
try {
$client = new Client();
$response = $client->post(
$url,
[
'headers' => [
'Content-Type' => "application/json",
],
'body' => json_encode([
"username" => $usr,
"avatar_url" => $av,
"content" => "$message"
])
]
);
} catch(GuzzleException $e) {
Core::addFlash('error', 'Trying to send an event, the following error occurred: ' . $e->getMessage());
}
}
}
}