Version 5.1 - All the GraphQL #32

Closed
timw4mail wants to merge 1160 commits from develop into master
22 changed files with 347 additions and 451 deletions
Showing only changes of commit 67819156ed - Show all commits

View File

@ -14,6 +14,7 @@
* @link https://github.com/timw4mail/HummingBirdAnimeClient * @link https://github.com/timw4mail/HummingBirdAnimeClient
*/ */
use function Aviat\AnimeClient\loadToml;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Lower level configuration // Lower level configuration
@ -23,7 +24,9 @@
$APP_DIR = realpath(__DIR__ . '/../'); $APP_DIR = realpath(__DIR__ . '/../');
$ROOT_DIR = realpath("{$APP_DIR}/../"); $ROOT_DIR = realpath("{$APP_DIR}/../");
$base_config = [ $tomlConfig = loadToml(__DIR__);
$base_config = array_merge($tomlConfig, [
'asset_dir' => "{$ROOT_DIR}/public", 'asset_dir' => "{$ROOT_DIR}/public",
// Template file path // Template file path
@ -34,6 +37,5 @@ $base_config = [
'img_cache_path' => "{$ROOT_DIR}/public/images", 'img_cache_path' => "{$ROOT_DIR}/public/images",
// Included config files // Included config files
'menus' => require 'menus.php',
'routes' => require 'routes.php', 'routes' => require 'routes.php',
]; ]);

View File

@ -1,41 +0,0 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
*
* PHP version 7
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2017 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://github.com/timw4mail/HummingBirdAnimeClient
*/
return [
'anime_list' => [
'route_prefix' => '/anime',
'items' => [
'watching' => '/watching',
'plan_to_watch' => '/plan_to_watch',
'on_hold' => '/on_hold',
'dropped' => '/dropped',
'completed' => '/completed',
'all' => '/all'
]
],
'manga_list' => [
'route_prefix' => '/manga',
'items' => [
'reading' => '/reading',
'plan_to_read' => '/plan_to_read',
'on_hold' => '/on_hold',
'dropped' => '/dropped',
'completed' => '/completed',
'all' => '/all'
]
]
];

19
app/appConf/menus.toml Normal file
View File

@ -0,0 +1,19 @@
[anime_list]
route_prefix = "/anime"
[anime_list.items]
watching = '/watching'
plan_to_watch = '/plan_to_watch'
on_hold = '/on_hold'
dropped = '/dropped'
completed = '/completed'
all = '/all'
[manga_list]
route_prefix = "/manga"
[manga_list.items]
reading = '/reading'
plan_to_read = '/plan_to_read'
on_hold = '/on_hold'
dropped = '/dropped'
completed = '/completed'
all = '/all'

View File

@ -0,0 +1,19 @@
################################################################################
# Route config
#
# Default views and paths
################################################################################
# Path to public directory, where images/css/javascript are located,
# appended to the url
asset_path = "/public"
# Which list should be the default?
default_lis = "anime" # anime or manga
# Default pages for anime/manga
default_anime_list_path = "watching" # watching|plan_to_watch|on_hold|dropped|completed|all
default_manga_list_path = "reading" # reading|plan_to_read|on_hold|dropped|completed|all
# Default view type (cover_view/list_view)
default_view_type = "cover_view"

View File

@ -22,33 +22,12 @@ use const Aviat\AnimeClient\{
use Aviat\AnimeClient\AnimeClient; use Aviat\AnimeClient\AnimeClient;
// -------------------------------------------------------------------------
// Routing Config
//
// Maps paths to controlers and methods
// -------------------------------------------------------------------------
return [ return [
// -------------------------------------------------------------------------
// Routing options
//
// Specify default paths and views
// -------------------------------------------------------------------------
'route_config' => [
// Path to public directory, where images/css/javascript are located,
// appended to the url
'asset_path' => '/public',
// Which list should be the default?
'default_list' => 'anime', // anime or manga
// Default pages for anime/manga
'default_anime_list_path' => "watching", // watching|plan_to_watch|on_hold|dropped|completed|all
'default_manga_list_path' => "reading", // reading|plan_to_read|on_hold|dropped|completed|all
// Default view type (cover_view/list_view)
'default_view_type' => 'cover_view',
],
// -------------------------------------------------------------------------
// Routing Config
//
// Maps paths to controlers and methods
// -------------------------------------------------------------------------
'routes' => [
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Anime List Routes // Anime List Routes
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -228,5 +207,4 @@ return [
'controller' => DEFAULT_CONTROLLER_NAMESPACE, 'controller' => DEFAULT_CONTROLLER_NAMESPACE,
'action' => 'redirectToDefaultRoute', 'action' => 'redirectToDefaultRoute',
], ],
],
]; ];

View File

@ -30,7 +30,11 @@
<tr id="a-<?= $item['id'] ?>"> <tr id="a-<?= $item['id'] ?>">
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<td> <td>
<a class="bracketed" href="<?= $urlGenerator->url("/anime/edit/{$item['id']}/{$item['watching_status']}") ?>">Edit</a> <a class="bracketed" href="<?= $url->generate('edit', [
'controller' => 'anime',
'id' => $item['id'],
'status' => $item['watching_status']
]) ?>">Edit</a>
</td> </td>
<?php endif ?> <?php endif ?>
<td class="justify"> <td class="justify">

View File

@ -21,8 +21,11 @@
<div class="table"> <div class="table">
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<div class="row"> <div class="row">
<span class="edit"><a class="bracketed" href="<?= $urlGenerator->url("collection/edit/{$item['hummingbird_id']}") ?>">Edit</a></span> <span class="edit">
<?php /*<span class="delete"><a class="bracketed" href="<?= $urlGenerator->url("collection/delete/{$item['hummingbird_id']}") ?>">Delete</a></span> */ ?> <a class="bracketed" href="<?= $url->generate('collection.edit.get', [
'id' => $item['hummingbird_id']
]) ?>">Edit</a>
</span>
</div> </div>
<?php endif ?> <?php endif ?>
<div class="row"> <div class="row">

View File

@ -1,6 +1,6 @@
<main> <main>
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $urlGenerator->fullUrl('collection/add', 'anime') ?>">Add Item</a> <a class="bracketed" href="<?= $url->generate('collection.add.get') ?>">Add Item</a>
<?php endif ?> <?php endif ?>
<?php if (empty($sections)): ?> <?php if (empty($sections)): ?>
<h3>There's nothing here!</h3> <h3>There's nothing here!</h3>
@ -26,12 +26,11 @@
<tr> <tr>
<?php if($auth->isAuthenticated()): ?> <?php if($auth->isAuthenticated()): ?>
<td> <td>
<a class="bracketed" href="<?= $urlGenerator->fullUrl("collection/edit/{$item['hummingbird_id']}") ?>">Edit</a> <a class="bracketed" href="<?= $url->generate('collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
<?php /*<a class="bracketed" href="<?= $urlGenerator->fullUrl("collection/delete/{$item['hummingbird_id']}") ?>">Delete</a>*/ ?>
</td> </td>
<?php endif ?> <?php endif ?>
<td class="align_left"> <td class="align_left">
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>"> <a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
<?= $item['title'] ?> <?= $item['title'] ?>
</a> </a>
<?= ( ! empty($item['alternate_title'])) ? " <br /><small> " . $item['alternate_title'] . "</small>" : "" ?> <?= ( ! empty($item['alternate_title'])) ? " <br /><small> " . $item['alternate_title'] . "</small>" : "" ?>

View File

@ -6,11 +6,11 @@
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> List <?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> List
</a> </a>
<?php if($config->get("show_{$url_type}_collection")): ?> <?php if($config->get("show_{$url_type}_collection")): ?>
[<a href="<?= $urlGenerator->url('collection/view') ?>"><?= ucfirst($url_type) ?> Collection</a>] [<a href="<?= $url->generate('collection.view') ?>"><?= ucfirst($url_type) ?> Collection</a>]
<?php endif ?> <?php endif ?>
[<a href="<?= $urlGenerator->defaultUrl($other_type) ?>"><?= ucfirst($other_type) ?> List</a>] [<a href="<?= $urlGenerator->defaultUrl($other_type) ?>"><?= ucfirst($other_type) ?> List</a>]
<?php else: ?> <?php else: ?>
<a href="<?= $urlGenerator->url('collection/view') ?>"> <a href="<?= $url->generate('collection.view') ?>">
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> Collection <?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> Collection
</a> </a>
[<a href="<?= $urlGenerator->defaultUrl('anime') ?>">Anime List</a>] [<a href="<?= $urlGenerator->defaultUrl('anime') ?>">Anime List</a>]

View File

@ -1,6 +1,6 @@
<main> <main>
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a> <a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
<?php endif ?> <?php endif ?>
<?php if (empty($sections)): ?> <?php if (empty($sections)): ?>
<h3>There's nothing here!</h3> <h3>There's nothing here!</h3>
@ -14,6 +14,7 @@
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<div class="edit_buttons" hidden> <div class="edit_buttons" hidden>
<button class="plus_one_chapter">+1 Chapter</button> <button class="plus_one_chapter">+1 Chapter</button>
<?php /* <button class="plus_one_volume">+1 Volume</button> */ ?>
</div> </div>
<?php endif ?> <?php endif ?>
<img src="<?= $escape->attr($item['manga']['image']) ?>" /> <img src="<?= $escape->attr($item['manga']['image']) ?>" />
@ -29,7 +30,15 @@
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<div class="row"> <div class="row">
<span class="edit"> <span class="edit">
<a class="bracketed" title="Edit information about this manga" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a> <a class="bracketed"
title="Edit information about this manga"
href="<?= $url->generate('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">
Edit
</a>
</span> </span>
</div> </div>
<?php endif ?> <?php endif ?>

View File

@ -1,6 +1,6 @@
<main> <main>
<?php if ($auth->isAuthenticated()): ?> <?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a> <a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
<?php endif ?> <?php endif ?>
<?php if (empty($sections)): ?> <?php if (empty($sections)): ?>
<h3>There's nothing here!</h3> <h3>There's nothing here!</h3>
@ -27,7 +27,11 @@
<tr id="manga-<?= $item['id'] ?>"> <tr id="manga-<?= $item['id'] ?>">
<?php if($auth->isAuthenticated()): ?> <?php if($auth->isAuthenticated()): ?>
<td> <td>
<a class="bracketed" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a> <a class="bracketed" href="<?= $url->generate('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">Edit</a>
</td> </td>
<?php endif ?> <?php endif ?>
<td class="align_left"> <td class="align_left">

View File

@ -55,6 +55,7 @@ class Controller {
'config' => $this->config 'config' => $this->config
]); ]);
$this->url = $auraUrlGenerator;
$this->urlGenerator = $urlGenerator; $this->urlGenerator = $urlGenerator;
$session = $container->get('session'); $session = $container->get('session');
@ -133,7 +134,7 @@ class Controller {
} }
$this->setFlashMessage('Invalid username or password.'); $this->setFlashMessage('Invalid username or password.');
$this->redirect($this->urlGenerator->url('login'), 303); $this->redirect($this->url->generate('login'), 303);
} }
/** /**

View File

@ -116,7 +116,7 @@ class Anime extends BaseController {
$this->config->get('whose_list') . "'s Anime List", $this->config->get('whose_list') . "'s Anime List",
'Add' 'Add'
), ),
'action_url' => $this->urlGenerator->url('anime/add'), 'action_url' => $this->url->generate('anime.add.post'),
'status_list' => AnimeWatchingStatus::KITSU_TO_TITLE 'status_list' => AnimeWatchingStatus::KITSU_TO_TITLE
]); ]);
} }
@ -168,8 +168,9 @@ class Anime extends BaseController {
), ),
'item' => $item, 'item' => $item,
'statuses' => AnimeWatchingStatus::KITSU_TO_TITLE, 'statuses' => AnimeWatchingStatus::KITSU_TO_TITLE,
'action' => $this->container->get('url-generator') 'action' => $this->url->generate('update.post', [
->url('/anime/update_form'), 'controller' => 'anime'
]),
]); ]);
} }

View File

@ -41,18 +41,6 @@ class Collection extends BaseController {
*/ */
private $animeModel; private $animeModel;
/**
* Data to be sent to all routes in this controller
* @var array $baseData
*/
protected $baseData;
/**
* Url Generator class
* @var UrlGenerator
*/
protected $urlGenerator;
/** /**
* Constructor * Constructor
* *
@ -62,7 +50,6 @@ class Collection extends BaseController {
{ {
parent::__construct($container); parent::__construct($container);
$this->urlGenerator = $container->get('url-generator');
$this->animeModel = $container->get('anime-model'); $this->animeModel = $container->get('anime-model');
$this->animeCollectionModel = $container->get('anime-collection-model'); $this->animeCollectionModel = $container->get('anime-collection-model');
$this->baseData = array_merge($this->baseData, [ $this->baseData = array_merge($this->baseData, [
@ -118,10 +105,11 @@ class Collection extends BaseController {
$this->setSessionRedirect(); $this->setSessionRedirect();
$action = (is_null($id)) ? "Add" : "Edit"; $action = (is_null($id)) ? "Add" : "Edit";
$urlAction = strtolower($action);
$this->outputHTML('collection/' . strtolower($action), [ $this->outputHTML('collection/' . $urlAction, [
'action' => $action, 'action' => $action,
'action_url' => $this->urlGenerator->fullUrl('collection/' . strtolower($action)), 'action_url' => $this->url->generate("collection.{$urlAction}.post"),
'title' => $this->formatTitle( 'title' => $this->formatTitle(
$this->config->get('whose_list') . "'s Anime Collection", $this->config->get('whose_list') . "'s Anime Collection",
$action $action

View File

@ -106,7 +106,7 @@ class Manga extends Controller {
$this->config->get('whose_list') . "'s Manga List", $this->config->get('whose_list') . "'s Manga List",
'Add' 'Add'
), ),
'action_url' => $this->urlGenerator->url('manga/add'), 'action_url' => $this->url->generate('manga.add.post'),
'status_list' => $statuses 'status_list' => $statuses
]); ]);
} }
@ -159,8 +159,9 @@ class Manga extends Controller {
'title' => $title, 'title' => $title,
'status_list' => MangaReadingStatus::KITSU_TO_TITLE, 'status_list' => MangaReadingStatus::KITSU_TO_TITLE,
'item' => $item, 'item' => $item,
'action' => $this->container->get('url-generator') 'action' => $this->url->generate('update.post', [
->url('/manga/update_form'), 'controller' => 'manga'
]),
]); ]);
} }

View File

@ -65,6 +65,12 @@ trait ControllerTrait {
*/ */
protected $urlGenerator; protected $urlGenerator;
/**
* Aura url generator
* @var \Aura\Router\Generator
*/
protected $url;
/** /**
* Session segment * Session segment
* @var \Aura\Session\Segment * @var \Aura\Session\Segment

View File

@ -16,10 +16,7 @@
namespace Aviat\AnimeClient; namespace Aviat\AnimeClient;
use Aviat\Ion\ use Aviat\Ion\{ArrayWrapper, StringWrapper};
{
ArrayWrapper, StringWrapper
};
use Aviat\Ion\Di\ContainerInterface; use Aviat\Ion\Di\ContainerInterface;
/** /**

View File

@ -59,9 +59,8 @@ class RoutingBase {
{ {
$this->container = $container; $this->container = $container;
$this->config = $container->get('config'); $this->config = $container->get('config');
$baseRoutes = $this->config->get('routes'); $this->routes = $this->config->get('routes');
$this->routes = $baseRoutes['routes']; $this->routeConfig = $this->config->get('route_config');
$this->routeConfig = $baseRoutes['route_config'];
} }
/** /**

View File

@ -108,32 +108,5 @@ class UrlGenerator extends RoutingBase {
throw new InvalidArgumentException("Invalid default type: '{$type}'"); throw new InvalidArgumentException("Invalid default type: '{$type}'");
} }
/**
* Generate full url path from the route path based on config
*
* @param string $path - (optional) The route path
* @param string $type - (optional) The controller (anime or manga), defaults to anime
* @return string
*/
public function fullUrl(string $path = "", string $type = "anime"): string
{
$configDefaultRoute = $this->__get("default_{$type}_path");
// Remove beginning/trailing slashes
$path = trim($path, '/');
// Set the default view
if ($path === '')
{
$path .= trim($configDefaultRoute, '/');
if ($this->__get('default_to_list_view'))
{
$path .= '/list';
}
}
return $this->url($path);
}
} }
// End of UrlGenerator.php // End of UrlGenerator.php

View File

@ -95,13 +95,11 @@ class AnimeClientTestCase extends TestCase {
'file' => ':memory:', 'file' => ':memory:',
] ]
], ],
'routes' => [
'route_config' => [ 'route_config' => [
'asset_path' => '/assets' 'asset_path' => '/assets'
], ],
'routes' => [ 'routes' => [
]
], ],
'mal' => [ 'mal' => [
'username' => 'foo', 'username' => 'foo',

View File

@ -71,7 +71,6 @@ class DispatcherTest extends AnimeClientTestCase {
public function dataRoute() public function dataRoute()
{ {
$defaultConfig = [ $defaultConfig = [
'routes' => [
'routes' => [ 'routes' => [
'login_form' => [ 'login_form' => [
'path' => '/login', 'path' => '/login',
@ -104,7 +103,6 @@ class DispatcherTest extends AnimeClientTestCase {
'manga_path' => 'manga', 'manga_path' => 'manga',
'default_list' => 'anime' 'default_list' => 'anime'
] ]
],
]; ];
$data = [ $data = [
@ -134,8 +132,8 @@ class DispatcherTest extends AnimeClientTestCase {
] ]
]; ];
$data['manga_default_routing_anime']['config']['routes']['route_config']['default_list'] = 'manga'; $data['manga_default_routing_anime']['config']['route_config']['default_list'] = 'manga';
$data['manga_default_routing_manga']['config']['routes']['route_config']['default_list'] = 'manga'; $data['manga_default_routing_manga']['config']['route_config']['default_list'] = 'manga';
return $data; return $data;
} }
@ -169,7 +167,6 @@ class DispatcherTest extends AnimeClientTestCase {
public function testDefaultRoute() public function testDefaultRoute()
{ {
$config = [ $config = [
'routes' => [
'route_config' => [ 'route_config' => [
'anime_path' => 'anime', 'anime_path' => 'anime',
'manga_path' => 'manga', 'manga_path' => 'manga',
@ -201,7 +198,6 @@ class DispatcherTest extends AnimeClientTestCase {
] ]
] ]
] ]
]
]; ];
$this->doSetUp($config, "/", "localhost"); $this->doSetUp($config, "/", "localhost");
@ -217,7 +213,6 @@ class DispatcherTest extends AnimeClientTestCase {
return [ return [
'controller_list_sanity_check' => [ 'controller_list_sanity_check' => [
'config' => [ 'config' => [
'routes' => [
'routes' => [ 'routes' => [
], ],
@ -228,7 +223,6 @@ class DispatcherTest extends AnimeClientTestCase {
'default_manga_list_path' => 'all', 'default_manga_list_path' => 'all',
'default_list' => 'manga' 'default_list' => 'manga'
], ],
]
], ],
'expected' => [ 'expected' => [
'anime' => 'Aviat\AnimeClient\Controller\Anime', 'anime' => 'Aviat\AnimeClient\Controller\Anime',
@ -239,7 +233,6 @@ class DispatcherTest extends AnimeClientTestCase {
], ],
'empty_controller_list' => [ 'empty_controller_list' => [
'config' => [ 'config' => [
'routes' => [
'routes' => [ 'routes' => [
], ],
@ -250,7 +243,6 @@ class DispatcherTest extends AnimeClientTestCase {
'default_manga_path' => '/manga/all', 'default_manga_path' => '/manga/all',
'default_list' => 'manga' 'default_list' => 'manga'
], ],
]
], ],
'expected' => [ 'expected' => [
'anime' => 'Aviat\AnimeClient\Controller\Anime', 'anime' => 'Aviat\AnimeClient\Controller\Anime',

View File

@ -49,60 +49,4 @@ class UrlGeneratorTest extends AnimeClientTestCase {
$result = $urlGenerator->assetUrl(...$args); $result = $urlGenerator->assetUrl(...$args);
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
public function dataFullUrl()
{
return [
'default_view' => [
'config' => [
'routes' => [
'routes' => [],
'route_config' => [
'anime_path' => 'anime',
'manga_path' => 'manga',
'default_list' => 'manga',
'default_anime_path' => '/anime/watching',
'default_manga_path' => '/manga/all',
'default_to_list_view' => FALSE,
]
],
],
'path' => '',
'type' => 'manga',
'expected' => '//localhost/manga/all',
],
'default_view_list' => [
'config' => [
'routes' => [
'routes' => [],
'route_config' => [
'anime_path' => 'anime',
'manga_path' => 'manga',
'default_list' => 'manga',
'default_anime_path' => '/anime/watching',
'default_manga_path' => '/manga/all',
'default_to_list_view' => TRUE,
]
],
],
'path' => '',
'type' => 'manga',
'expected' => '//localhost/manga/all/list',
]
];
}
/**
* @dataProvider dataFullUrl
*/
public function testFullUrl($config, $path, $type, $expected)
{
$config = new Config($config);
$this->container->setInstance('config', $config);
$urlGenerator = new UrlGenerator($this->container);
$result = $urlGenerator->fullUrl($path, $type);
$this->assertEquals($expected, $result);
}
} }