Refactor EVERYTHING, for better, more configurable code

This commit is contained in:
Timothy Warren 2015-06-11 16:44:52 -04:00
parent 5c461adcc1
commit aedfc743c6
27 changed files with 529 additions and 285 deletions

57
app/base/BaseApiModel.php Normal file
View File

@ -0,0 +1,57 @@
<?php
use \GuzzleHttp\Client;
use \GuzzleHttp\Cookie\CookieJar;
/**
* Base model for api interaction
*/
class BaseApiModel extends BaseModel {
/**
* The Guzzle http client object
* @var object $client
*/
protected $client;
/**
* Cookie jar object for api requests
* @var object $cookieJar
*/
protected $cookieJar;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->cookieJar = new CookieJar();
$this->client = new Client([
'base_url' => $this->base_url,
'defaults' => [
'cookies' => $this->cookieJar,
'headers' => [
'User-Agent' => $_SERVER['HTTP_USER_AGENT'],
'Accept-Encoding' => 'application/json'
],
'timeout' => 5,
'connect_timeout' => 5
]
]);
}
/**
* Get the full url path, since the base_url in Guzzle doesn't work correctly
*
* @param string $path
* @return string
*/
protected function _url($path)
{
return "{$this->base_url}{$path}";
}
}
// End of BaseApiModel.php

View File

@ -1,9 +1,19 @@
<?php <?php
/**
* Base class for controllers, defines output methods
*/
class BaseController { class BaseController {
/**
* The global configuration object
* @var object $config
*/
protected $config; protected $config;
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
global $config; global $config;
@ -23,7 +33,7 @@ class BaseController {
$route = $router->get_route(); $route = $router->get_route();
$data['route_path'] = ($route) ? $router->get_route()->path : ""; $data['route_path'] = ($route) ? $router->get_route()->path : "";
$path = realpath(__DIR__ . "/../views/{$template}.php"); $path = _dir(APP_DIR, 'views', "{$template}.php");
if ( ! is_file($path)) if ( ! is_file($path))
{ {
@ -32,6 +42,7 @@ class BaseController {
ob_start(); ob_start();
extract($data); extract($data);
include _dir(APP_DIR, 'views', 'header.php');
include $path; include $path;
$buffer = ob_get_contents(); $buffer = ob_get_contents();
ob_end_clean(); ob_end_clean();
@ -57,4 +68,5 @@ class BaseController {
header("Content-type: application/json"); header("Content-type: application/json");
echo $data; echo $data;
} }
} }
// End of BaseController.php

28
app/base/BaseDBModel.php Normal file
View File

@ -0,0 +1,28 @@
<?php
/**
* Base model for database interaction
*/
class BaseDBModel extends BaseModel {
/**
* The query builder object
* @var object $db
*/
protected $db;
/**
* The database connection information array
* @var array $db_config
*/
protected $db_config;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->db_config = $this->config->database;
}
}
// End of BaseDBModel.php

View File

@ -1,43 +1,23 @@
<?php <?php
use \GuzzleHttp\Client; /**
use \GuzzleHttp\Cookie\CookieJar; * Common base for all Models
*/
class BaseModel { class BaseModel {
protected $client; /**
* The global configuration object
* @var object $config
*/
protected $config; protected $config;
protected $cookieJar;
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
global $config; global $config;
$this->config = $config; $this->config = $config;
$this->cookieJar = new CookieJar();
$this->client = new Client([
'base_url' => $this->base_url,
'defaults' => [
'cookies' => $this->cookieJar,
'headers' => [
'User-Agent' => $_SERVER['HTTP_USER_AGENT'],
'Accept-Encoding' => 'application/json'
],
'timeout' => 5,
'connect_timeout' => 5
]
]);
}
/**
* Get the full url path, since the base_url in Guzzle doesn't work correctly
*
* @param string $path
* @return string
*/
protected function _url($path)
{
return "{$this->base_url}{$path}";
} }
/** /**

View File

@ -2,10 +2,31 @@
use Aura\Router\RouterFactory; use Aura\Router\RouterFactory;
/**
* Basic routing/ dispatch
*/
class Router { class Router {
/**
* The route-matching object
* @var object $router
*/
protected $router;
/**
* The global configuration object
* @var object $config
*/
protected $config;
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
global $config;
$this->config = $config;
$router_factory = new RouterFactory(); $router_factory = new RouterFactory();
$router = $router_factory->newInstance(); $router = $router_factory->newInstance();
$this->router = $router_factory->newInstance(); $this->router = $router_factory->newInstance();
@ -20,8 +41,17 @@ class Router {
*/ */
public function get_route() public function get_route()
{ {
//$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); global $defaultHandler;
$route = $this->router->match($_SERVER['REQUEST_URI'], $_SERVER);
$raw_route = $_SERVER['REQUEST_URI'];
$route_path = str_replace([$this->config->anime_path, $this->config->manga_path], '', $raw_route);
$route_path = "/" . trim($route_path, '/');
$defaultHandler->addDataTable('Route Info', [
'route_path' => $route_path
]);
$route = $this->router->match($route_path, $_SERVER);
return $route; return $route;
} }
@ -54,7 +84,6 @@ class Router {
'title' => 'Page Not Found' 'title' => 'Page Not Found'
] ]
]; ];
} }
else else
{ {
@ -69,22 +98,25 @@ class Router {
call_user_func_array([$controller, $action_method], $params); call_user_func_array([$controller, $action_method], $params);
} }
/**
* Select controller based on the current url, and apply its relevent routes
*
* @return void
*/
private function _setup_routes() private function _setup_routes()
{ {
$host = $_SERVER['HTTP_HOST']; $route_type = "anime";
$route_type = "";
switch($host)
{
case "anime.timshomepage.net":
$route_type = "anime";
break;
case "manga.timshomepage.net": if ($this->config->manga_host !== "" && strpos($_SERVER['HTTP_HOST'], $this->config->manga_host) !== FALSE)
$route_type = "manga"; {
break; $route_type = "manga";
}
else if ($this->config->manga_path !== "" && strpos($_SERVER['REQUEST_URI'], $this->config->manga_path) !== FALSE)
{
$route_type = "manga";
} }
$routes = require __DIR__ . '/../config/routes.php'; $routes = $this->config->routes;
// Add routes by the configuration file // Add routes by the configuration file
foreach($routes[$route_type] as $name => $route) foreach($routes[$route_type] as $name => $route)

View File

@ -1,17 +0,0 @@
<?php
function anime_autoloader($class) {
$dirs = ["base", "controllers", "models"];
foreach($dirs as $dir)
{
$file = realpath(__DIR__ . "/../{$dir}/{$class}.php");
if (file_exists($file))
{
require_once $file;
return;
}
}
}
spl_autoload_register('anime_autoloader');

47
app/base/functions.php Normal file
View File

@ -0,0 +1,47 @@
<?php
/**
* Global functions
*/
/**
* HTML selection helper function
*
* @param string $a - First item to compare
* @param string $b - Second item to compare
* @return string
*/
function is_selected($a, $b)
{
return ($a === $b) ? 'selected' : '';
}
/**
* Generate full url path from the route path based on config
*
* @param string $path - The route path
* @param [string] $host - The controller (anime or manga), defaults to anime
* @return string
*/
function full_url($path, $type="anime")
{
global $config;
$config_path = $config->{"{$type}_path"};
$config_host = $config->{"{$type}_host"};
// Remove beginning/trailing slashes
$config_path = trim($config_path, '/');
$path = trim($path, '/');
$host = ($config_host !== '') ? $config_host : $_SERVER['HTTP_HOST'];
if ($config_path !== '')
{
$path = "{$config_path}/{$path}";
}
return "//{$host}/{$path}";
}
// End of functions.php

View File

@ -2,8 +2,24 @@
return (object)[ return (object)[
// Username for feeds // Username for feeds
'hummingbird_username' => 'timw4mail', 'hummingbird_username' => 'timw4mail',
// ----------------------------------------------------------------------------
// Routing
//
// Route by path, or route by domain. To route by path, set the _host suffixed
// options to an empty string. To route by host, set the _path suffixed options
// to an empty string
// ----------------------------------------------------------------------------
'anime_host' => 'anime.timshomepage.net',
'manga_host' => 'manga.timshomepage.net',
'anime_path' => '',
'manga_path' => '',
// Cache paths // Cache paths
'data_cache_path' => __DIR__ . '/../cache', 'data_cache_path' => _dir(APP_DIR, 'cache'),
'img_cache_path' => __DIR__ .'/../../public/images' 'img_cache_path' => _dir(ROOT_DIR, 'public/images'),
// Included config files
'routes' => require _dir(CONF_DIR, 'routes.php'),
'database' => require _dir(CONF_DIR, 'database.php'),
]; ];

View File

@ -2,21 +2,31 @@
return [ return [
'anime' => [ 'anime' => [
'index' => [
'path' => '/',
'controller' => 'AnimeController'
],
'all' => [ 'all' => [
'path' => '/all', 'path' => '/all',
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'all' 'action' => 'anime_list',
'params' => [
'type' => 'all',
'title' => WHOSE . " Anime List &middot All"
]
],
'index' => [
'path' => '/',
'controller' => 'AnimeController',
'action' => 'anime_list',
'params' => [
'type' => 'currently-watching',
'title' => WHOSE . " Anime List &middot Watching"
]
], ],
'plan_to_watch' => [ 'plan_to_watch' => [
'path' => '/plan_to_watch', 'path' => '/plan_to_watch',
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'anime_list', 'action' => 'anime_list',
'params' => [ 'params' => [
'type' => 'plan-to-watch' 'type' => 'plan-to-watch',
'title' => WHOSE . " Anime List &middot Plan to Watch"
] ]
], ],
'on_hold' => [ 'on_hold' => [
@ -24,7 +34,8 @@ return [
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'anime_list', 'action' => 'anime_list',
'params' => [ 'params' => [
'type' => 'on-hold' 'type' => 'on-hold',
'title' => WHOSE . " Anime List &middot On Hold"
] ]
], ],
'dropped' => [ 'dropped' => [
@ -32,7 +43,8 @@ return [
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'anime_list', 'action' => 'anime_list',
'params' => [ 'params' => [
'type' => 'dropped' 'type' => 'dropped',
'title' => WHOSE . " Anime List &middot Dropped"
] ]
], ],
'completed' => [ 'completed' => [
@ -40,7 +52,8 @@ return [
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'anime_list', 'action' => 'anime_list',
'params' => [ 'params' => [
'type' => 'completed' 'type' => 'completed',
'title' => WHOSE . " Anime List &middot Completed"
] ]
], ],
'collection' => [ 'collection' => [
@ -48,29 +61,34 @@ return [
'controller' => 'AnimeController', 'controller' => 'AnimeController',
'action' => 'collection', 'action' => 'collection',
'params' => [] 'params' => []
],
'anime_login' => [
'path' => '/login',
'controller' => 'AnimeController',
'action' => 'login'
] ]
], ],
'manga' => [ 'manga' => [
'index' => [
'path' => '/',
'controller' => 'MangaController'
],
'all' => [ 'all' => [
'path' => '/all', 'path' => '/all',
'controller' => 'MangaController', 'controller' => 'MangaController',
'action' => 'all' 'action' => 'manga_list',
'params' => [
'type' => 'all',
'title' => WHOSE . " Manga List &middot; All"
]
],
'index' => [
'path' => '/',
'controller' => 'MangaController',
'action' => 'manga_list',
'params' => [
'type' => 'Reading',
'title' => WHOSE . " Manga List &middot; Reading"
]
], ],
'plan_to_read' => [ 'plan_to_read' => [
'path' => '/plan_to_read', 'path' => '/plan_to_read',
'controller' => 'MangaController', 'controller' => 'MangaController',
'action' => 'manga_list', 'action' => 'manga_list',
'params' => [ 'params' => [
'type' => 'Plan to Read' 'type' => 'Plan to Read',
'title' => WHOSE . " Manga List &middot; Plan to Read"
] ]
], ],
'on_hold' => [ 'on_hold' => [
@ -78,7 +96,8 @@ return [
'controller' => 'MangaController', 'controller' => 'MangaController',
'action' => 'manga_list', 'action' => 'manga_list',
'params' => [ 'params' => [
'type' => 'On Hold' 'type' => 'On Hold',
'title' => WHOSE . " Manga List &middot; On Hold"
] ]
], ],
'dropped' => [ 'dropped' => [
@ -86,7 +105,8 @@ return [
'controller' => 'MangaController', 'controller' => 'MangaController',
'action' => 'manga_list', 'action' => 'manga_list',
'params' => [ 'params' => [
'type' => 'Dropped' 'type' => 'Dropped',
'title' => WHOSE . " Manga List &middot; Dropped"
] ]
], ],
'completed' => [ 'completed' => [
@ -94,7 +114,8 @@ return [
'controller' => 'MangaController', 'controller' => 'MangaController',
'action' => 'manga_list', 'action' => 'manga_list',
'params' => [ 'params' => [
'type' => 'Completed' 'type' => 'Completed',
'title' => WHOSE . " Manga List &middot; Completed"
] ]
], ],
] ]

View File

@ -1,10 +1,39 @@
<?php <?php
/**
* Controller for Anime-related pages
*/
class AnimeController extends BaseController { class AnimeController extends BaseController {
/**
* The anime list model
* @var object $model
*/
private $model; private $model;
/**
* The anime collection model
* @var object $collection_model
*/
private $collection_model; private $collection_model;
/**
* Route mapping for main navigation
* @var array $nav_routes
*/
private $nav_routes = [
'Watching' => '/',
'Plan to Watch' => '/plan_to_watch',
'On Hold' => '/on_hold',
'Dropped' => '/dropped',
'Completed' => '/completed',
'Collection' => '/collection',
'All' => '/all'
];
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -12,35 +41,38 @@ class AnimeController extends BaseController {
$this->collection_model = new AnimeCollectionModel(); $this->collection_model = new AnimeCollectionModel();
} }
public function index() /**
* Show a portion, or all of the anime list
*
* @param string $type - The section of the list
* @param string $title - The title of the page
* @return void
*/
public function anime_list($type, $title)
{ {
$this->anime_list('currently-watching'); $data = ($type != 'all')
} ? $this->model->get_list($type)
: $this->model->get_all_lists();
public function all() $this->outputHTML('anime/list', [
{
$data = $this->model->get_all_lists();
$this->outputHTML('anime_list', [
'title' => "Tim's Anime List &middot; All",
'sections' => $data
]);
}
public function anime_list($type, $title="Tim's Anime List")
{
$data = $this->model->get_list($type);
$this->outputHTML('anime_list', [
'title' => $title, 'title' => $title,
'nav_routes' => $this->nav_routes,
'sections' => $data 'sections' => $data
]); ]);
} }
/**
* Show the anime collection page
*
* @return void
*/
public function collection() public function collection()
{ {
$data = $this->collection_model->get_collection(); $data = $this->collection_model->get_collection();
$this->outputHTML('anime_collection', [ $this->outputHTML('anime/collection', [
'title' => "Tim's Anime Collection", 'title' => WHOSE . " Anime Collection",
'nav_routes' => $this->nav_routes,
'sections' => $data 'sections' => $data
]); ]);
} }

View File

@ -1,42 +1,56 @@
<?php <?php
/**
* Controller for manga list
*/
class MangaController extends BaseController { class MangaController extends BaseController {
/**
* The manga model
* @var object $model
*/
private $model; private $model;
/**
* Route mapping for main navigation
* @var array $nav_routes
*/
private $nav_routes = [
'Reading' => '/',
'Plan to Read' => '/plan_to_read',
'On Hold' => '/on_hold',
'Dropped' => '/dropped',
'Completed' => '/completed',
'All' => '/all'
];
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->model = new MangaModel(); $this->model = new MangaModel();
} }
public function index() /**
* Get a section of the manga list
*
* @param string $status
* @param string $title
* @return void
*/
public function manga_list($status, $title)
{ {
$this->manga_list('Reading'); $data = ($status !== 'all')
} ? [$status => $this->model->get_list($status)]
: $this->model->get_all_lists();
public function all() $this->outputHTML('manga/list', [
{ 'title' => $title,
$data = $this->model->get_all_lists(); 'nav_routes' => $this->nav_routes,
$this->outputHTML('manga_list', [
'title' => "Tim's Manga List &middot; All",
'sections' => $data 'sections' => $data
]); ]);
} }
public function manga_list($type, $title="Tim's Manga List")
{
$data = $this->model->get_list($type);
$this->outputHTML('manga_list', [
'title' => $title,
'sections' => [$type => $data]
]);
}
public function login()
{
$data = $this->model->authenticate();
//print_r($data);
}
} }
// End of MangaController.php // End of MangaController.php

View File

@ -3,21 +3,24 @@
/** /**
* Model for getting anime collection data * Model for getting anime collection data
*/ */
class AnimeCollectionModel extends BaseModel { class AnimeCollectionModel extends BaseDBModel {
protected $base_url = "";
private $db;
private $anime_model;
private $db_config;
/**
* Anime API Model
* @var object $anime_model
*/
private $anime_model;
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
$this->db_config = require_once(__DIR__ . '/../config/database.php');
$this->db = Query($this->db_config['collection']);
$this->anime_model = new AnimeModel();
parent::__construct(); parent::__construct();
$this->db = Query($this->db_config['collection']);
$this->anime_model = new AnimeModel();
// Do an import if an import file exists // Do an import if an import file exists
$this->json_import(); $this->json_import();
} }

View File

@ -3,11 +3,16 @@
/** /**
* Model for handling requests dealing with the anime list * Model for handling requests dealing with the anime list
*/ */
class AnimeModel extends BaseModel { class AnimeModel extends BaseApiModel {
protected $client; /**
protected $cookieJar; * The base url for api requests
* @var string $base_url
*/
protected $base_url = "https://hummingbird.me/api/v1"; protected $base_url = "https://hummingbird.me/api/v1";
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -134,7 +139,7 @@ class AnimeModel extends BaseModel {
$output = $response->json(); $output = $response->json();
$output_json = json_encode($output); $output_json = json_encode($output);
if (file_get_contents($cache_file) !== $output_json) if (( ! file_exists($cache_file)) || file_get_contents($cache_file) !== $output_json)
{ {
// Cache the call in case of downtime // Cache the call in case of downtime
file_put_contents($cache_file, json_encode($output)); file_put_contents($cache_file, json_encode($output));

View File

@ -1,14 +1,17 @@
<?php <?php
/** /**
* Model for handling requests dealing with the manga list * Model for handling requests dealing with the manga list
*/ */
class MangaModel extends BaseModel { class MangaModel extends BaseApiModel {
protected $client; /**
protected $cookieJar; * @var string $base_url - The base url for api requests
*/
protected $base_url = "https://hummingbird.me"; protected $base_url = "https://hummingbird.me";
/**
* Constructor
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@ -56,7 +59,7 @@ class MangaModel extends BaseModel {
{ {
global $defaultHandler; global $defaultHandler;
$cache_file = __DIR__ . "/../cache/manga.json"; $cache_file = _dir($this->config->data_cache_path, 'manga.json');
$config = [ $config = [
'query' => [ 'query' => [

View File

@ -1,4 +1,3 @@
<?php include 'header.php'; ?>
<body> <body>
<main> <main>
<h1>404</h1> <h1>404</h1>

View File

@ -1,19 +1,19 @@
<?php include 'header.php' ?> <body class="anime collection">
<body> <h1><?= WHOSE ?> Anime Collection [<a href="<?= full_url('', 'manga') ?>">Manga List</a>]</h1>
<h1>Tim's Anime List [<a href="//manga.timshomepage.net">Manga List</a>]</h1> <?php include 'nav.php' ?>
<?php include 'anime_nav.php' ?>
<main> <main>
<?php foreach ($sections as $name => $items): ?> <?php foreach ($sections as $name => $items): ?>
<section class="status"> <section class="status">
<h2><?= $name ?></h2> <h2><?= $name ?></h2>
<section class="media-wrap"> <section class="media-wrap">
<?php foreach($items as $item): ?> <?php foreach($items as $item): ?>
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>">
<article class="media" id="a-<?= $item['hummingbird_id'] ?>"> <article class="media" id="a-<?= $item['hummingbird_id'] ?>">
<img src="<?= $item['cover_image'] ?>" /> <img src="<?= $item['cover_image'] ?>" />
<div class="name"><a href="https://hummingbird.me/anime/<?= $item['slug'] ?>"> <div class="name">
<?= $item['title'] ?> <?= $item['title'] ?>
<?= ($item['alternate_title'] != "") ? "<br />({$item['alternate_title']})" : ""; ?> <?= ($item['alternate_title'] != "") ? "<br />({$item['alternate_title']})" : ""; ?>
</a></div> </div>
<div class="media_metadata"> <div class="media_metadata">
<div class="completion">Episodes: <?= $item['episode_count'] ?></div> <div class="completion">Episodes: <?= $item['episode_count'] ?></div>
</div> </div>
@ -22,6 +22,7 @@
<div class="age_rating"><?= $item['age_rating'] ?></div> <div class="age_rating"><?= $item['age_rating'] ?></div>
</div> </div>
</article> </article>
</a>
<?php endforeach ?> <?php endforeach ?>
</section> </section>
</section> </section>

View File

@ -1,5 +1,5 @@
<body> <body>
<?php include 'anime_nav.php' ?> <?php include 'nav.php' ?>
<main> <main>
<form action="<?= $action ?>" method="post"> <form action="<?= $action ?>" method="post">
</form> </form>

34
app/views/anime/list.php Normal file
View File

@ -0,0 +1,34 @@
<body class="anime list">
<h1><?= WHOSE ?> Anime List [<a href="<?= full_url('', 'manga') ?>">Manga List</a>]</h1>
<?php include 'nav.php' ?>
<main>
<?php foreach ($sections as $name => $items): ?>
<section class="status">
<h2><?= $name ?></h2>
<section class="media-wrap">
<?php foreach($items as $item): ?>
<a href="<?= $item['anime']['url'] ?>">
<article class="media" id="a-<?= $item['anime']['id'] ?>">
<img class="round_all" src="<?= $item['anime']['cover_image'] ?>" />
<div class="round_all name">
<?= $item['anime']['title'] ?>
<?= ($item['anime']['alternate_title'] != "") ? "<br />({$item['anime']['alternate_title']})" : ""; ?>
</div>
<div class="media_metadata">
<div class="round_top airing_status"><?= $item['anime']['status'] ?></div>
<div class="user_rating"><?= (int)($item['rating']['value'] * 2) ?> / 10</div>
<div class="round_bottom completion">Episodes: <?= $item['episodes_watched'] ?> / <?= $item['anime']['episode_count'] ?></div>
</div>
<div class="medium_metadata">
<div class="round_top media_type"><?= $item['anime']['show_type'] ?></div>
<div class="round_bottom age_rating"><?= $item['anime']['age_rating'] ?></div>
</div>
</article>
</a>
<?php endforeach ?>
</section>
</section>
<?php endforeach ?>
</main>
</body>
</html>

7
app/views/anime/nav.php Normal file
View File

@ -0,0 +1,7 @@
<nav>
<ul>
<?php foreach($nav_routes as $title => $path): ?>
<li class="<?= is_selected($path, $route_path) ?>"><a href="<?= full_url($path) ?>"><?= $title ?></a></li>
<?php endforeach ?>
</ul>
</nav>

View File

@ -1,33 +0,0 @@
<?php include 'header.php' ?>
<body>
<h1>Tim's Anime List [<a href="//manga.timshomepage.net">Manga List</a>]</h1>
<?php include 'anime_nav.php' ?>
<main>
<?php foreach ($sections as $name => $items): ?>
<section class="status">
<h2><?= $name ?></h2>
<section class="media-wrap">
<?php foreach($items as $item): ?>
<article class="media" id="a-<?= $item['anime']['id'] ?>">
<img src="<?= $item['anime']['cover_image'] ?>" />
<div class="name"><a href="<?= $item['anime']['url'] ?>">
<?= $item['anime']['title'] ?>
<?= ($item['anime']['alternate_title'] != "") ? "<br />({$item['anime']['alternate_title']})" : ""; ?>
</a></div>
<div class="media_metadata">
<div class="airing_status"><?= $item['anime']['status'] ?></div>
<div class="user_rating"><?= (int)($item['rating']['value'] * 2) ?> / 10</div>
<div class="completion">Episodes: <?= $item['episodes_watched'] ?> / <?= $item['anime']['episode_count'] ?></div>
</div>
<div class="medium_metadata">
<div class="media_type"><?= $item['anime']['show_type'] ?></div>
<div class="age_rating"><?= $item['anime']['age_rating'] ?></div>
</div>
</article>
<?php endforeach ?>
</section>
</section>
<?php endforeach ?>
</main>
</body>
</html>

View File

@ -1,11 +0,0 @@
<nav>
<ul>
<li class="<?= is_selected('/', $route_path) ?>"><a href="/">Watching</a></li>
<li class="<?= is_selected('/plan_to_watch', $route_path) ?>"><a href="/plan_to_watch">Plan to Watch</a></li>
<li class="<?= is_selected('/on_hold', $route_path) ?>"><a href="/on_hold">On Hold</a></li>
<li class="<?= is_selected('/dropped', $route_path) ?>"><a href="/dropped">Dropped</a></li>
<li class="<?= is_selected('/completed', $route_path) ?>"><a href="/completed">Completed</a></li>
<li class="<?= is_selected('/collection', $route_path) ?>"><a href="/collection">Collection</a></li>
<li class="<?= is_selected('/all', $route_path) ?>"><a href="/all">All</a></li>
</ul>
</nav>

View File

@ -1,28 +1,20 @@
<!DOCTYPE html> <body class="manga list">
<html lang="en"> <h1><?= WHOSE ?> Manga List [<a href="<?= full_url("", "anime") ?>">Anime List</a>]</h1>
<head> <?php include 'nav.php' ?>
<title><?= $title ?></title>
<link rel="stylesheet" href="/public/css/marx.css" />
<link rel="stylesheet" href="/public/css/base.css" />
<link rel="stylesheet" href="/public/css/manga.css" />
</head>
<body>
<h1>Tim's Manga List [<a href="//anime.timshomepage.net">Anime List</a>]</h1>
<?php include 'manga_nav.php' ?>
<main> <main>
<?php foreach ($sections as $name => $items): ?> <?php foreach ($sections as $name => $items): ?>
<section class="status"> <section class="status">
<h2><?= $name ?></h2> <h2><?= $name ?></h2>
<section class="media-wrap"> <section class="media-wrap">
<?php foreach($items as $item): ?> <?php foreach($items as $item): ?>
<a href="https://hummingbird.me/manga/<?= $item['manga']['id'] ?>">
<article class="media" id="manga-<?= $item['manga']['id'] ?>"> <article class="media" id="manga-<?= $item['manga']['id'] ?>">
<img src="<?= $item['manga']['poster_image'] ?>" /> <img src="<?= $item['manga']['poster_image'] ?>" />
<div class="name"><a href="https://hummingbird.me/manga/<?= $item['manga']['id'] ?>"> <div class="name">
<?= $item['manga']['romaji_title'] ?> <?= $item['manga']['romaji_title'] ?>
<?= (isset($item['manga']['english_title'])) ? "<br />({$item['manga']['english_title']})" : ""; ?> <?= (isset($item['manga']['english_title'])) ? "<br />({$item['manga']['english_title']})" : ""; ?>
</a></div> </div>
<div class="media_metadata"> <div class="media_metadata">
<?php /*<div class="airing_status"><?= $item['manga']['status'] ?></div>*/ ?>
<div class="user_rating"><?= ($item['rating'] > 0) ? (int)($item['rating'] * 2) : '-' ?> / 10</div> <div class="user_rating"><?= ($item['rating'] > 0) ? (int)($item['rating'] * 2) : '-' ?> / 10</div>
<div class="completion"> <div class="completion">
Chapters: <?= $item['chapters_read'] ?> / <?= ($item['manga']['chapter_count'] > 0) ? $item['manga']['chapter_count'] : "-" ?><?php /*<br /> Chapters: <?= $item['chapters_read'] ?> / <?= ($item['manga']['chapter_count'] > 0) ? $item['manga']['chapter_count'] : "-" ?><?php /*<br />
@ -33,6 +25,7 @@
<div class="media_type"><?= $item['manga']['manga_type'] ?></div> <div class="media_type"><?= $item['manga']['manga_type'] ?></div>
</div> */ ?> </div> */ ?>
</article> </article>
</a>
<?php endforeach ?> <?php endforeach ?>
</section> </section>
</section> </section>

7
app/views/manga/nav.php Normal file
View File

@ -0,0 +1,7 @@
<nav>
<ul>
<?php foreach($nav_routes as $title => $path): ?>
<li class="<?= is_selected($path, $route_path) ?>"><a href="<?= full_url($path, 'manga') ?>"><?= $title ?></a></li>
<?php endforeach ?>
</ul>
</nav>

View File

@ -1,10 +0,0 @@
<nav>
<ul>
<li class="<?= is_selected('/', $route_path) ?>"><a href="/">Reading</a></li>
<li class="<?= is_selected('/plan_to_read', $route_path) ?>"><a href="/plan_to_read">Plan to Read</a></li>
<li class="<?= is_selected('/on_hold', $route_path) ?>"><a href="/on_hold">On Hold</a></li>
<li class="<?= is_selected('/dropped', $route_path) ?>"><a href="/dropped">Dropped</a></li>
<li class="<?= is_selected('/completed', $route_path) ?>"><a href="/completed">Completed</a></li>
<li class="<?= is_selected('/all', $route_path) ?>"><a href="/all">All</a></li>
</ul>
</nav>

View File

@ -1,20 +1,47 @@
<?php <?php
require __DIR__ . '/vendor/autoload.php'; /**
require __DIR__ . '/app/base/autoloader.php'; * Joins paths together. Variadic to take an
* arbitrary number of arguments
*
* @return string
*/
function _dir() { return implode(DIRECTORY_SEPARATOR, func_get_args()); }
// Base paths
define('ROOT_DIR', __DIR__);
define('APP_DIR', _dir(ROOT_DIR, 'app'));
define('CONF_DIR', _dir(APP_DIR, 'config'));
define('BASE_DIR', _dir(APP_DIR, 'base'));
// Who's list is it?
define('WHOSE', "Tim's");
// Load config and global functions
$config = require _dir(APP_DIR, '/config/config.php');
require _dir(BASE_DIR, '/functions.php');
// Setup autoloaders
require _dir(ROOT_DIR, '/vendor/autoload.php');
spl_autoload_register(function ($class) {
$dirs = ["base", "controllers", "models"];
foreach($dirs as $dir)
{
$file = _dir(APP_DIR, $dir, "{$class}.php");
if (file_exists($file))
{
require_once $file;
return;
}
}
});
session_start(); session_start();
use \Whoops\Handler\PrettyPageHandler; use \Whoops\Handler\PrettyPageHandler;
use \Whoops\Handler\JsonResponseHandler; use \Whoops\Handler\JsonResponseHandler;
function is_selected($a, $b)
{
return ($a === $b) ? 'selected' : '';
}
$config = require_once(__DIR__ . '/app/config/config.php');
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Setup error handling // Setup error handling
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -36,7 +63,7 @@ $whoops->register();
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
$router = new Router(); $router = new Router();
//$defaultHandler->addDataTable('route', (array)$router->get_route()); $defaultHandler->addDataTable('route', (array)$router->get_route());
$router->dispatch(); $router->dispatch();
// End of index.php // End of index.php

View File

@ -2,6 +2,22 @@ body {
margin: 0.5em; margin: 0.5em;
} }
.round_all {
border-radius:0.5em;
}
.round_top {
border-radius: 0;
border-top-right-radius:0.5em;
border-top-left-radius:0.5em;
}
.round_bottom {
border-radius: 0;
border-bottom-right-radius:0.5em;
border-bottom-left-radius:0.5em;
}
.media-wrap { .media-wrap {
text-align:center; text-align:center;
margin:0 auto; margin:0 auto;
@ -16,9 +32,6 @@ body {
height:319px; height:319px;
margin:0.25em; margin:0.25em;
} }
.media > img {
border-radius:0.5em;
}
.name, .media_type, .airing_status, .user_rating, .completion, .age_rating { .name, .media_type, .airing_status, .user_rating, .completion, .age_rating {
text-shadow: 1px 2px 1px rgba(0, 0, 0, 0.85); text-shadow: 1px 2px 1px rgba(0, 0, 0, 0.85);
@ -27,7 +40,7 @@ body {
padding:0.25em; padding:0.25em;
text-align:right; text-align:right;
} }
.media_type, .age_rating { .media_type, .age_rating {
text-align:left; text-align:left;
} }
@ -37,34 +50,22 @@ body {
bottom:0; bottom:0;
right:0; right:0;
} }
.media > .medium_metadata { .media > .medium_metadata {
position:absolute; position:absolute;
bottom: 0; bottom: 0;
left:0; left:0;
} }
.media > .media_metadata > .airing_status,
.media > .medium_metadata > .media_type
{
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
}
.media > .media_metadata > .completion,
.media > .medium_metadata > .age_rating
{
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
}
.media > .name { .media > .name {
border-radius:0.5em;
position:absolute; position:absolute;
top: 0; top: 0;
} }
.media > .name:hover { .media:hover > .name,
.media:hover > .media_metadata > div,
.media:hover > .medium_metadata > div
{
background:rgba(0,0,0,0.9); background:rgba(0,0,0,0.9);
} }
@ -78,3 +79,28 @@ body {
.user_rating::before { .user_rating::before {
content: "Rating: "; content: "Rating: ";
} }
/* -----------------------------------------------------------------------------
Manga-list-specific styles
------------------------------------------------------------------------------*/
.manga .media > .name {
padding:0.5em;
margin:1em;
}
.manga .media {
border:1px solid #ddd;
width:200px;
height:290px;
margin:0.25em;
}
.manga .completion::before {
content: "";
}
.manga .media_metadata {
padding: 0.25em;
margin: 0.75em;
}

View File

@ -1,29 +0,0 @@
.media,
.media > .name,
.media > img,
.media > .media_metadata > .user_rating,
.media > .media_metadata > .completion
{
border-radius: 0;
}
.media > .name {
padding:0.5em;
margin:1em;
}
.media {
border:1px solid #ddd;
width:200px;
height:290px;
margin:0.25em;
}
.completion::before {
content: "";
}
.media_metadata {
padding: 0.25em;
margin: 0.75em;
}