ytkns/src/Zone.php
2020-06-10 16:03:13 +00:00

362 lines
11 KiB
PHP

<?php
namespace YTKNS;
use Exception;
class ZoneNotFoundException extends Exception {};
class ZoneCreationFailedException extends Exception {};
class ZoneInvalidIdException extends Exception {};
class ZoneInvalidNameException extends Exception {};
class ZoneInvalidTitleException extends Exception {};
final class Zone {
private $effects = null;
private $passiveMode = false;
public function __construct() {
}
public function setPassiveMode(): void {
$this->passiveMode = true;
if($this->effects === null)
$this->effects = [];
}
public function getId(): int {
return $this->zone_id ?? 0;
}
public function hasId(): bool {
return isset($this->zone_id) && $this->zone_id > 0;
}
private function setId(int $id): void {
if($id < 1)
throw new ZoneInvalidIdException;
$this->zone_id = $id;
}
public function getUserId(): int {
return $this->user_id ?? 0;
}
public function setUserId(int $userId): void {
$this->user_id = $userId;
}
private $userObj = null;
public function getUser(): User {
if($this->userObj === null)
$this->userObj = User::byId($this->getUserId());
return $this->userObj;
}
public function getName(): string {
return $this->zone_name;
}
public function setName(string $name): void {
if(!self::validName($name))
throw new ZoneInvalidNameException;
$this->zone_name = $name;
}
public function getUrl(): string {
return 'https://' . sprintf(Config::get('domain.zone'), $this->getName());
}
public function getUrlForPreview(): string {
return $this->getUrl() . '?preview=1';
}
public function getScreenshotPath(): string {
return YTKNS_SCREENSHOTS . '/' . $this->getName() . '.jpg';
}
public function getScreenshotUrl(): string {
return 'https://' . Config::get('domain.main') . '/ss/' . $this->getName() . '.jpg';
}
public function takeScreenshot(): void {
$path = escapeshellarg($this->getScreenshotPath());
$url = escapeshellarg($this->getUrlForPreview());
system(sprintf('/usr/bin/firefox --window-size=800,600 --screenshot %s %s', $path, $url));
//system(sprintf('/usr/bin/convert %1$s 200x150 %1$s', $path));
}
public function removeScreenshot(): void {
$path = $this->getScreenshotPath();
if(is_file($path))
unlink($path);
}
public function getTitle(): string {
return $this->zone_title;
}
public function setTitle(string $title): void {
if(strlen($title) > 255)
throw new ZoneInvalidTitleException;
$this->zone_title = $title;
}
public function getViews(): int {
return $this->zone_views ?? 0;
}
public function incrementViews(): void {
$updateViews = DB::prepare('
UPDATE `ytkns_zones`
SET `zone_views` = `zone_views` + 1
WHERE `zone_id` = :zone
');
$updateViews->bindValue('zone', $this->getId());
if($updateViews->execute())
$this->zone_views = $this->getViews() + 1;
}
public function getEffects(): array {
if(!is_array($this->effects))
$this->effects = $this->hasId() ? ZoneEffect::byZone($this) : [];
return $this->effects;
}
public function getPageBuilder(bool $quiet = false): PageBuilder {
$pageBuilder = new PageBuilder($this->getTitle());
$effects = $this->getEffects();
foreach($effects as $effect)
$effect->applyEffect($pageBuilder, $quiet);
return $pageBuilder;
}
public function addEffect(PageEffectInterface $effect): void {
if(is_array($this->effects))
$this->effects[] = $effect;
if(!$this->passiveMode)
$this->addEffectDatabase($effect);
}
private function addEffectDatabase(PageEffectInterface $effect): void {
if(!$this->hasId())
return;
$insert = DB::prepare('
REPLACE INTO `ytkns_zones_effects` (
`zone_id`, `effect_name`, `effect_params`
) VALUES (
:zone, :name, :params
)
');
$insert->bindValue('zone', $this->getId());
$insert->bindValue('name', substr(get_class($effect), 14, -6));
$insert->bindValue('params', json_encode($effect->getEffectParams()));
$insert->execute();
}
public function removeEffects(): void {
$this->effects = [];
if(!$this->passiveMode)
$this->removeEffectsDatabase();
}
private function removeEffectsDatabase(): void {
if(!$this->hasId())
return;
$removeEffects = DB::prepare('
DELETE FROM `ytkns_zones_effects`
WHERE `zone_id` = :zone
');
$removeEffects->bindValue('zone', $this->getId());
$removeEffects->execute();
}
public static function byId(string $id): self {
$getZone = DB::prepare('
SELECT `zone_id`, `user_id`, `zone_name`, `zone_title`, `zone_views`,
UNIX_TIMESTAMP(`zone_created`) AS `zone_created`,
UNIX_TIMESTAMP(`zone_updated`) AS `zone_updated`
FROM `ytkns_zones`
WHERE `zone_id` = :zone
');
$getZone->bindValue('zone', $id);
$zone = $getZone->execute() ? $getZone->fetchObject(self::class) : false;
if(!$zone)
throw new ZoneNotFoundException;
return $zone;
}
public static function byName(string $name): self {
$getZone = DB::prepare('
SELECT `zone_id`, `user_id`, `zone_name`, `zone_title`, `zone_views`,
UNIX_TIMESTAMP(`zone_created`) AS `zone_created`,
UNIX_TIMESTAMP(`zone_updated`) AS `zone_updated`
FROM `ytkns_zones`
WHERE `zone_name` = :name
');
$getZone->bindValue('name', $name);
$zone = $getZone->execute() ? $getZone->fetchObject(self::class) : false;
if(!$zone)
throw new ZoneNotFoundException;
return $zone;
}
public static function byUser(User $user, ?string $orderBy = null, bool $ascending = true, int $take = 0, int $offset = 0): array {
$getZonesQuery = '
SELECT `zone_id`, `user_id`, `zone_name`, `zone_title`, `zone_views`,
UNIX_TIMESTAMP(`zone_created`) AS `zone_created`,
UNIX_TIMESTAMP(`zone_updated`) AS `zone_updated`
FROM `ytkns_zones`
WHERE `user_id` = :user
';
if($orderBy !== null)
$getZonesQuery .= sprintf('ORDER BY `%s` %s', $orderBy, $ascending ? 'ASC' : 'DESC');
if($take > 0)
$getZonesQuery .= sprintf(' LIMIT %d OFFSET %d', $take, $offset);
$getZones = DB::prepare($getZonesQuery);
$getZones->bindValue('user', $user->getId());
$getZones->execute();
$zones = [];
while($zone = $getZones->fetchObject(self::class))
$zones[] = $zone;
return $zones;
}
public static function all(?string $orderBy = null, bool $ascending = true, int $take = 0, int $offset = 0): array {
$getZonesQuery = '
SELECT `zone_id`, `user_id`, `zone_name`, `zone_title`, `zone_views`,
UNIX_TIMESTAMP(`zone_created`) AS `zone_created`,
UNIX_TIMESTAMP(`zone_updated`) AS `zone_updated`
FROM `ytkns_zones`
';
if($orderBy !== null)
$getZonesQuery .= sprintf(' ORDER BY `%s` %s', $orderBy, $ascending ? 'ASC' : 'DESC');
if($take > 0)
$getZonesQuery .= sprintf(' LIMIT %d OFFSET %d', $take, $offset);
$getZones = DB::prepare($getZonesQuery);
$getZones->execute();
$zones = [];
while($zone = $getZones->fetchObject(self::class))
$zones[] = $zone;
return $zones;
}
public static function count(): int {
$getZoneCount = DB::prepare('
SELECT COUNT(`zone_id`)
FROM `ytkns_zones`
');
return (int)($getZoneCount->execute() ? $getZoneCount->fetchColumn() : 0);
}
public static function create(User $user, string $name, string $title): self {
$create = DB::prepare('
INSERT INTO `ytkns_zones` (
`user_id`, `zone_name`, `zone_title`
) VALUES (
:user, LOWER(:name), :title
)
');
$create->bindValue('user', $user->getId());
$create->bindValue('name', $name);
$create->bindValue('title', $title);
$create->execute();
try {
return self::byName($name);
} catch(ZoneNotFoundException $ex) {
throw new ZoneCreationFailedException;
}
}
public function update(array $save = ['user_id', 'zone_name', 'zone_title']): void {
if(!$this->hasId())
return;
$set = [
'`zone_updated` = NOW()',
];
foreach($save as $param)
$set[] = sprintf('`%1$s` = :%1$s', $param);
$update = DB::prepare(sprintf('UPDATE `ytkns_zones` SET %s WHERE `zone_id` = :zone', implode(', ', $set)));
if(in_array('user_id', $save))
$update->bindValue('user_id', $this->getUserId());
if(in_array('zone_name', $save))
$update->bindValue('zone_name', $this->getName());
if(in_array('zone_title', $save))
$update->bindValue('zone_title', $this->getTitle());
$update->bindValue('zone', $this->getId());
$update->execute();
if($this->passiveMode) {
$effects = $this->getEffects();
$this->removeEffectsDatabase();
foreach($this->getEffects() as $effectInfo)
$this->addEffectDatabase($effectInfo);
}
}
public static function exists(string $name): bool {
$check = DB::prepare('
SELECT COUNT(`zone_name`) > 0
FROM `ytkns_zones`
WHERE `zone_name` = :name
');
$check->bindValue('name', $name);
return $check->execute() ? (bool)$check->fetchColumn() : true;
}
public function delete(): void {
$delete = DB::prepare('
DELETE FROM `ytkns_zones`
WHERE `zone_id` = :zone
');
$delete->bindValue('zone', $this->getId());
$delete->execute();
}
public static function validName(string $name): bool {
return preg_match('#^([A-Za-z0-9]{1,50})$#', $name);
}
public function toJson(bool $asString = false) {
$zoneInfo = [
'id' => $this->getId(),
'name' => $this->getName(),
'title' => $this->getTitle(),
'effects' => [],
];
foreach($this->getEffects() as $effect)
$zoneInfo['effects'][] = $effect->toJson();
if($asString)
$zoneInfo = json_encode($zoneInfo);
return $zoneInfo;
}
public function queueTask(string $task, ...$params): void {
ZoneTask::enqueue($this, $task, $params);
}
}