Compare commits

..

1178 Commits

Author SHA1 Message Date
7afdab80ac Update frontend minification config and dependencies
All checks were successful
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good
2024-02-15 15:21:26 -05:00
144718866d Merge branch 'develop'
All checks were successful
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good
2024-01-23 13:35:40 -05:00
1dd9fdd0c2 Attmept to fix issue with Stringy 2024-01-17 08:32:48 -05:00
3794ed20a8 Update CI to use several PHP versions 2023-12-21 13:49:54 -05:00
ff8c837fd9 Update views and templates to use render helper object 2023-12-21 13:46:15 -05:00
fe1caffc0f Simplify setup of rendering methods by putting them in a wrapper class 2023-12-21 13:19:59 -05:00
8e7b2a04fd Fix broken test 2023-10-27 09:28:06 -04:00
0e684736bd Vastly simplify logic for getting a user's anime library. Most basic API functionality seems to be working 2023-10-26 16:00:13 -04:00
1ad4427584 Start of migration to amphp/http-client 5.0 2023-10-26 11:10:21 -04:00
b0a16e7730 Pin latest working version of Aura Router 2023-10-25 07:42:13 -04:00
7559f79ef6 Various tweaks 2023-10-02 11:48:34 -04:00
c8b642be1c Add anilist example config file, and make the interface a bit more helpful 2023-07-13 14:17:51 -04:00
91c435cdac Update header comments 2023-07-13 11:08:05 -04:00
57249882ab Some minor code style fixes 2023-07-13 11:06:52 -04:00
c28dfc09b3 Update js/css dependencies 2023-06-28 15:48:33 -04:00
cd5841c4c8 Merge pull request 'default config and documentation update' (#43) from colwellkr/HummingBirdAnimeClient:develop into develop
Reviewed-on: #43
2023-06-28 15:24:25 -04:00
c83b30f43a Merge branch 'develop' into develop 2023-06-28 15:23:56 -04:00
011160a3c2 add php extensions required to README.md 2023-06-28 18:37:44 +00:00
067d888f4c Fix route issue for manga pages 2023-06-28 14:30:30 -04:00
1b5a5080f2 add default theme to config.toml.example 2023-06-28 18:22:00 +00:00
1a97277ec4 Tweak a Stringy method to have a more sensible return type 2023-06-28 14:03:15 -04:00
e426fdd4a3 Fix a load of code cleanliness and style issues 2023-05-19 16:34:38 -04:00
5758de7667 Update tooling and config 2023-05-19 16:34:07 -04:00
d21d9a86d4 Update frontend dependencies, remove period on update banner 2023-05-19 16:33:14 -04:00
a435162a2b Update rector config 2023-05-19 11:00:12 -04:00
4fef2b2942 Fix some remaining formatting issues 2023-05-19 10:57:59 -04:00
77c6419c80 Rector updates for tests 2023-05-19 10:56:23 -04:00
97fe3b4b40 PHP 8.1 syntax updates 2023-05-19 10:54:08 -04:00
5396091b83 Move new helper functions to a more logical place, fix UserTransformer test 2023-05-19 10:42:25 -04:00
655ad119ce Fix broken test 2023-05-18 17:00:00 -04:00
351446a2ee Fix formatting 2023-05-18 16:27:31 -04:00
f2b7f61030 More user page tweaks 2023-05-18 16:25:50 -04:00
b678a3401e More local fixes, add some more data to user profile page 2023-05-18 15:17:19 -04:00
465cd99165 Add specific locale arguments to keep up with the API 2023-05-18 12:55:38 -04:00
2e67b49447 More code style updates 2023-05-09 12:52:11 -04:00
45449b6907 Code style updates 2023-05-09 12:49:36 -04:00
05d4fb1ad7 Style and tool updates 2023-05-09 12:46:52 -04:00
81d81f4393 Update aviat/query to fix PHP 8.2 deprecation notice 2023-03-17 16:38:41 -04:00
60ddcaa08e Make error handling for ajax list incrementing more robust 2023-03-17 11:09:43 -04:00
0e780f26b9 Update Kitsu GraphQL schema 2023-03-16 22:20:17 -04:00
bedd504b64 Update cache dependency 2023-03-16 22:19:56 -04:00
2f657dc20b Add Routing attributes to controllers for a potential future refactoring 2023-03-16 13:04:55 -04:00
4f8eefe71e Extract Stringy to fix deprecations 2023-03-16 13:03:48 -04:00
dd63767ddf Update DB dependency 2023-03-16 13:02:17 -04:00
e1ff1c6e21 Use MAL and Anlist IDs to do simultaneous updates and syncing. Resolves #39 2022-11-16 10:21:00 -05:00
f673a84cf6 More work on #39 2022-09-22 12:08:21 -04:00
4a2273c93c Fix tests 2022-09-22 11:13:53 -04:00
3cb90acb13 Fix incrementing with anilist 2022-09-22 10:21:09 -04:00
ab38e03af9 Update frontend tooling 2022-09-22 10:20:15 -04:00
2139f031f4 Pass anilist ids more directly, see #39 2022-09-21 15:36:38 -04:00
ec280cd76d Skip another test failing on CI 2022-06-29 11:28:12 -04:00
5e7f57eb0a Skip failing tests 2022-06-29 11:25:14 -04:00
26cbe4b592 Update broken test 2022-06-29 11:15:47 -04:00
e8aa2bd42b Bugfix for manga pages without characters 2022-06-28 18:57:50 -04:00
501062ac37 Update GraphQL schemas 2022-06-08 20:21:50 -04:00
4dc0bb29d2 Show completed message for the last episode/chapter of an anime/manga 2022-03-22 09:53:41 -04:00
b1c4c8cb5e Move header update script to tools folder 2022-03-04 15:52:18 -05:00
e4fe5bbfec Simplify file headers 2022-03-04 15:50:35 -05:00
4b35d25849 Update header comments 2022-03-04 12:32:17 -05:00
3b57d08c0a Tweak formatting settings 2022-03-04 12:31:13 -05:00
29e70a8e3f More formatting fixes 2022-03-04 12:25:29 -05:00
a0d30c002a Reformat test suite files 2022-03-04 12:19:47 -05:00
327065498b Reformat misc files 2022-03-03 18:19:02 -05:00
047ee4cb37 Set up a proper formatter for more consistent code style 2022-03-03 17:26:09 -05:00
9b945ca0a5 Remove more redundant phpdoc properties 2022-03-03 14:20:10 -05:00
e70b0fdb40 Fix tests 2022-03-03 13:52:06 -05:00
c8a38d5785 Remove a lot of redundant PHPDoc properties 2022-03-03 13:25:10 -05:00
10eb7794e9 Remove setup checks for removed directories 2022-01-17 10:22:46 -05:00
0dee5f52fc Remove now unused image caching directories 2022-01-17 10:19:20 -05:00
7195258d7e Update broken tests 2022-01-17 10:05:14 -05:00
9750d63e55 Use images directly from Kitsu for the rest of the views 2022-01-17 09:59:27 -05:00
e8a14fedf2 Update user details page to pull images directly from Kitsu 2022-01-17 08:58:59 -05:00
eb897adbd1 Update more views to use images directly from Kitsu 2022-01-17 08:37:00 -05:00
5fb1b87e67 Make all media images come directly from Kitsu API, including on the search pages 2022-01-12 18:23:40 -05:00
a9b24f0cf7 Update more views to use direct kitsu urls for poster images 2022-01-12 18:00:00 -05:00
f40ee254c9 Fix more cover image stuff 2022-01-10 17:19:28 -05:00
2b047634a0 Use kitsu urls for anime cover images 2022-01-10 17:06:05 -05:00
7baa5bfe91 Show images directly for anime detail pages, so cover images are pulled correctly 2022-01-10 16:56:10 -05:00
2d6058a6b3 Ensure that list items always have some sort of cover image 2022-01-08 13:04:24 -05:00
d34c79b4cd Remove unused placeholder images in public directory 2022-01-07 20:27:33 -05:00
ed558a6484 Use image builder class to simplify createPlaceholderImage 2022-01-07 19:57:07 -05:00
854987bd44 Create image builder class to simplify creating placeholder images 2022-01-07 19:53:31 -05:00
2977f7385e Merge pull request 'Fix table sorting algorithm' (#41) from colwellkr/HummingBirdAnimeClient:fix-sort into develop
Reviewed-on: #41
2022-01-07 17:04:19 -05:00
760cc71768 Merge branch 'develop' into fix-sort 2022-01-07 17:02:19 -05:00
573eac78f1 Removed whitespace from string.replace() call 2022-01-07 18:29:37 +00:00
02fa04d19d Code style fixes 2022-01-07 12:33:01 -05:00
6e1d190230 Update Anilist GraphQL schema 2022-01-06 12:56:46 -05:00
80b2495c11 Make Kitsu auth token error easier to recover from 2022-01-06 12:50:26 -05:00
3d9d8202d9 Updated sorting algorithm 2022-01-02 22:48:20 +00:00
188baa5cc8 Update "TV Rating" to "Age Rating" and add additional classes for sorting. 2022-01-02 21:29:08 +00:00
3dbaf7ef32 Update column headers for better consistency between pages 2022-01-02 21:16:34 +00:00
545984bb18 Allow adding items to anime collection that are in the anime list 2021-12-29 17:04:55 -05:00
f9a5716002 Fix testsuite by disabling tests with outdated data 2021-12-02 17:13:31 -05:00
818fcf114d Partially fix broken tests 2021-12-02 17:08:11 -05:00
3ca606d6f5 Update and streamline dependencies 2021-12-02 16:28:57 -05:00
288e64f357 Remove old CodeIgniter style sniffs, as it is not being used 2021-12-02 16:27:43 -05:00
6ed1b81451 Lots of little code fixes, hides notices shown on PHP 8.1 2021-12-02 16:06:34 -05:00
823ca8a805 sync Manga before Anime, as it's usually faster 2021-10-18 12:20:03 -04:00
7301c4852d Update library creation mutation to remove now redundant userID argument 2021-10-18 12:15:47 -04:00
602f0fc9c5 Update frontend dependencies, and update Kitsu graphql schema 2021-10-14 22:09:50 -04:00
694e7cc01c Remove redundant updates on finishing a media item 2021-10-08 22:55:54 -04:00
7efa34efee Update test snapshot for anime detail page with new info 2021-10-08 19:33:25 -04:00
49675ffbee Show full Language names on streaming info for anime detail pages 2021-10-08 19:32:10 -04:00
b82b7d74fc Add airing date range to anime detail pages 2021-10-08 19:31:40 -04:00
5102c7c459 Filter out titles not in English or Japanese from media cards 2021-10-08 18:28:30 -04:00
1d022bc8ae Show when a media item is already in the list when searching on the /add pages, resolves #38 2021-10-08 12:16:59 -04:00
489ec27602 Refactor media search rendering to be less redundant 2021-10-08 12:15:34 -04:00
6e4e065b75 Check user library when searching for new media 2021-10-08 12:06:08 -04:00
69db87e305 Attempt to fix Jenkins build, again 2021-10-07 21:51:29 -04:00
2dacca5d06 Make some anime detail pages more robust 2021-10-07 21:45:17 -04:00
1abac0ac0e Sort libraryEvents so watch history is in correct order 2021-07-30 09:37:06 -04:00
ba6ed8967c Update Kitsu GraphQL schema 2021-07-30 09:36:25 -04:00
3d80f755a1 Merge branch 'develop' 2021-04-23 19:23:08 -04:00
7a541b609f Fix build issue with phpstan 2021-04-23 19:22:45 -04:00
0f4383563f Merge remote-tracking branch 'origin/develop' 2021-04-23 19:01:21 -04:00
7b33d40de4 Update GraphQL reference schemas 2021-04-23 19:00:44 -04:00
4c85c22c30 Drastically simplify setup for bundling js files 2021-04-23 18:58:51 -04:00
7839cf1515 Even less floating, please 2021-04-21 20:20:39 -04:00
d2a9aaee54 We don't want our ratings to float... 2021-04-21 20:09:03 -04:00
ff85cb6153 A few minor tweaks 2021-04-21 19:35:22 -04:00
4c396ba9c6 Update filtering of MAL IDs for items to check to update 2021-04-21 19:33:51 -04:00
d2c397f6b9 Fix null error on updating Anime or Manga 2021-03-01 10:08:36 -05:00
e679322122 Coverage fix for Ion DI 2021-03-01 10:08:07 -05:00
e6ae6c9e9c Update Kitsu GraphQL schema 2021-03-01 10:06:12 -05:00
d387b793ea Misc fixes 2021-02-26 14:42:07 -05:00
50bb525f60 Replace Whoops with Tracy 2021-02-23 17:08:36 -05:00
8de60b332d Remove redundant docblocks from Type classes 2021-02-23 17:08:16 -05:00
51eb460ce9 Test Type classes 2021-02-23 15:38:29 -05:00
78a37c736f Attempt to fix the build with the right extension 2021-02-23 13:33:54 -05:00
380c455332 Use correct symfony polyfill 2021-02-23 13:15:37 -05:00
6c35ade209 Fix broken tests 2021-02-23 13:10:26 -05:00
633f30d365 Increase test coverage of Kitsu Transformer classes 2021-02-23 13:00:30 -05:00
8c1d882404 Improve test coverage 2021-02-23 12:00:22 -05:00
6af73cea55 Better handle update API errors 2021-02-22 15:39:03 -05:00
d3732d1a54 Update GraphQL schemas 2021-02-22 15:38:29 -05:00
3aecaf9161 Better catch api errors on incrementing progress 2021-02-22 15:37:35 -05:00
b12e94cee4 A few minor fixes 2021-02-18 12:48:59 -05:00
3d5d2c05ce Remove RoboFile 2021-02-18 07:37:33 -05:00
f01cc77f92 Remove some invalid exception docblock tags 2021-02-18 07:22:10 -05:00
c81271864d Fix Content Security Policy 2021-02-18 07:15:43 -05:00
10ea494594 Simplify search template functions 2021-02-18 07:14:07 -05:00
836b1d17e6 Use str_contains over strpos 2021-02-17 20:02:51 -05:00
5004a9f332 Update js build dependencies 2021-02-17 19:50:15 -05:00
40f134d7bc Merge remote-tracking branch 'origin/develop' 2021-02-17 13:07:02 -05:00
391708a49c Make PHPStan errors CI failures 2021-02-16 15:07:25 -05:00
566c9fbd1e Make PHPStan errors CI failures 2021-02-16 15:05:17 -05:00
56bda5ed71 Make PHPStan errors CI failures 2021-02-16 15:01:48 -05:00
d2fbd3b56a Make PHPStan errors CI failures 2021-02-16 14:58:33 -05:00
9f680a75e3 Make PHPStan errors CI failures 2021-02-16 14:57:43 -05:00
8fadf9d589 Make PHPStan errors CI failures 2021-02-16 14:56:05 -05:00
b64d8c6c5b Make PHPStan errors CI failures 2021-02-16 14:53:21 -05:00
b393c695a5 Resolve remaining PHPStan issues 2021-02-16 14:43:51 -05:00
1ab4dedddb Merge remote-tracking branch 'origin/develop' 2021-02-16 12:21:52 -05:00
73ee1a41e1 Fix more PHPStan issues 2021-02-12 19:17:39 -05:00
44a7d36174 Increase warning level of PHPStan 2021-02-12 17:52:58 -05:00
77f314ee55 Fix failing test 2021-02-12 13:14:58 -05:00
8d742e62ed Fix a bunch more phpstan errors 2021-02-12 13:09:57 -05:00
e1fd2bed59 Just go back to CheckStyle 2021-02-12 12:00:44 -05:00
61d44146cd Try to report phpStan errors more directly 2021-02-12 11:57:36 -05:00
b2761541ec Try to report phpStan errors more directly 2021-02-12 11:54:22 -05:00
6412f1108b Try to report phpStan errors more directly 2021-02-12 11:48:24 -05:00
c37f50d06e Try to report phpStan errors more directly 2021-02-12 11:43:25 -05:00
c900e379c5 Try to report phpStan errors more directly 2021-02-12 11:40:25 -05:00
326436c0b5 Try to report phpStan errors more directly 2021-02-12 11:27:20 -05:00
66df53bf43 Try to report phpStan errors more directly 2021-02-12 11:24:57 -05:00
2cd9f99011 Solve more PHPStan issues 2021-02-12 11:14:45 -05:00
35ec3c8bfa Fix code warnings for 'src/AnimeClient/AnimeClient.php' 2021-02-12 10:53:07 -05:00
52a0ff275d Attempt to add PHPStan messages to CI 2021-02-12 10:42:51 -05:00
a26c90ff86 Attempt to add PHPStan messages to CI 2021-02-12 10:40:03 -05:00
c173a8c196 Attempt to add PHPStan messages to CI 2021-02-12 10:38:16 -05:00
73cf8ccd5a Attempt to add PHPStan messages to CI 2021-02-12 10:34:08 -05:00
244372ff8c Attempt to add PHPStan messages to CI 2021-02-12 10:19:58 -05:00
74ef13713d Attempt to add PHPStan messages to CI 2021-02-12 10:07:55 -05:00
057d4bfae7 Attempt to add PHPStan messages to CI 2021-02-12 10:04:13 -05:00
68bc4693cb Attempt to add PHPStan messages to CI 2021-02-12 09:59:29 -05:00
ce3260a2f3 Attempt to add PHPStan messages to CI 2021-02-12 09:51:52 -05:00
4c5aa1e3de Attempt to add PHPStan messages to CI 2021-02-12 09:42:54 -05:00
3c8b564ab9 Attempt to add PHPStan messages to CI 2021-02-12 09:39:28 -05:00
7505907976 Yet more PHPStan fixes 2021-02-11 19:54:22 -05:00
05455a518b A few more PHPStan fixes 2021-02-10 17:31:20 -05:00
c39bc23061 Add a bumch of soundness checks suggested by PHPStan 2021-02-10 17:17:51 -05:00
9ba1bd4c90 Code style fixes 2021-02-10 13:59:37 -05:00
2f789cc4cf Add tests for title uniqueness check 2021-02-10 10:59:15 -05:00
a18c0bd7b5 Handle null values better in title uniqueness check 2021-02-08 17:03:04 -05:00
2b31cae57b Fix the CI build? 2021-02-07 08:54:20 -05:00
48dcaa4bcb Fix the CI build? 2021-02-05 20:21:05 -05:00
4342b2e795 Fix the CI build? 2021-02-05 20:16:55 -05:00
b2361c57c2 Fix the CI build? 2021-02-05 20:05:33 -05:00
e6fdea28d4 Fix the CI build? 2021-02-05 17:49:19 -05:00
15dcdfe39c Update docs and CI 2021-02-05 17:19:11 -05:00
0250a50731 Merge remote-tracking branch 'origin/develop' 2021-02-04 12:41:24 -05:00
f2aca2b76f Fix snapshot test 2021-02-04 12:35:01 -05:00
97a7d501d0 Fix tests? 2021-02-04 12:27:52 -05:00
8c3b583f92 Update PHP version in header comments 2021-02-04 11:57:01 -05:00
d6e174a014 Fix tests 2021-02-03 10:27:04 -05:00
df7a9e311b Merge pull request 'develop' (#37) from develop into master
Reviewed-on: #37
2021-02-03 10:07:11 -05:00
ebf22643ef Update CI to PHP 8 2021-02-03 09:52:18 -05:00
3039f412aa Move to PHP 8 2021-02-03 09:46:36 -05:00
37ab6034ba Cleanup some path related things 2021-02-03 09:45:18 -05:00
d5931ff53b Merge pull request 'Update header year code' (#36) from colwellkr/HummingBirdAnimeClient:develop into develop
Reviewed-on: #36
2021-01-14 12:50:22 -05:00
fe1250732c Update header year code 2021-01-13 01:58:28 -05:00
144e3f5229 Increase test coverage 2020-12-11 15:37:55 -05:00
5f494aa9bd Fix tests for PHP8...? 2020-12-11 14:26:54 -05:00
7e0cbe8b83 Make sure to run tests for PHP8 2020-12-11 10:26:24 -05:00
31ed9d11ab Fix anime collection error 2020-12-11 10:15:24 -05:00
6e3a70f9f6 Update dependencies 2020-12-11 10:14:59 -05:00
3c47570cce Bump version in header comments 2020-12-10 17:06:50 -05:00
b4d9e9f21f A little more test coverage 2020-12-10 17:04:45 -05:00
1ea5750a76 Refactor, increase test coverage 2020-12-10 15:59:37 -05:00
36b396be71 Update Kitsu GraphQL schema file 2020-12-02 12:43:04 -05:00
bf4f86a010 Add random anime and random manga pages 2020-12-02 12:42:47 -05:00
45b0209d8a Merge pull request 'All in GraphQL' (#34) from develop into master
Reviewed-on: #34
2020-12-01 10:07:48 -05:00
f37ec8022e Revert status mapping change 2020-10-21 21:23:35 -04:00
415778295f Fix broken test 2020-10-21 18:53:32 -04:00
ad0dcb5750 Remove some more dead code 2020-10-21 18:52:12 -04:00
e0ad68b9d2 Merge remote-tracking branch 'origin/master' into develop 2020-10-21 18:35:43 -04:00
608251452f More cleanup, update changelog 2020-10-21 18:12:22 -04:00
8c5547d69d More Kitsu GraphQL API cleanup, resolves #33 2020-10-21 17:59:43 -04:00
6c29af4533 Remove test for JsonAPI nonsense 2020-10-21 17:07:50 -04:00
898dfebbde Use GraphQL to update thumbnails, refactor GraphQL pagination, merge Anime and Manga traits back into the Kitsu model 2020-10-21 17:06:50 -04:00
2d5ae3b1c6 Use GraphQL search endpoints, see #33 2020-10-21 15:45:30 -04:00
8256815032 Remove old transformer classes 2020-10-21 15:02:25 -04:00
fe6f737815 Add missing GraphQL query 2020-10-21 14:56:33 -04:00
87d15024bb More GraphQL conversion, test updates, see #33 2020-10-21 14:51:17 -04:00
470d25f269 Sync Kitsu and Anilist both via GraphQL, see #33 2020-10-16 16:18:56 -04:00
70a33e36c0 Fetch Manga List via GraphQL, see #33 2020-10-16 13:28:35 -04:00
94d227b08e Fix Manga List Incrementing, start of GraphQL conversion 2020-10-12 14:06:49 -04:00
ecb913322f Pull anime lists from GraphQL, see #33 2020-10-09 16:18:45 -04:00
b001af868f Update dependency versions, add Amp base package as dependency 2020-10-09 16:16:23 -04:00
1fbf0283ba Fix updating anime status when certain fields are empty 2020-10-07 15:30:42 -04:00
5bcc046a12 Add back search query canceling for anime search 2020-10-07 09:10:11 -04:00
9009da4b86 Fix hiding anime on completion 2020-10-05 12:32:12 -04:00
47a4be2cf9 Update GraphQL queries to match API changes 2020-09-15 08:08:39 -04:00
52aabc2b12 Map more external sites 2020-09-10 15:36:34 -04:00
7b1217bafe Fix possible issue with hiding completed anime/manga 2020-09-10 15:35:43 -04:00
a79ab842ee Remove genres from manga list view 2020-09-09 13:26:31 -04:00
810731dfbd Update streaming logs, remove genres from anime list view 2020-09-09 13:25:27 -04:00
c224c8d977 Only show total length of a series if the number is positive 2020-09-09 10:24:55 -04:00
ce3e3427dc Update GraphQL schema for Kitsu 2020-09-09 10:24:12 -04:00
7211aa0de7 Add limit to all relationships 2020-09-09 10:23:17 -04:00
02bd0288f2 Do not check session unless already logged in 2020-08-28 14:27:14 -04:00
a15496e4a5 Sort voice acting roles by character name 2020-08-27 15:39:23 -04:00
a14ac3a122 Get Person detail pages via GraphQL, resolves #27 2020-08-27 15:01:00 -04:00
1a3f1e9654 More components, resolve #31 2020-08-26 17:26:42 -04:00
0c936b3fa7 Misc tweaks 2020-08-26 15:25:31 -04:00
ccb9c9d331 Extract common methods for Anime and Manga models into a trait 2020-08-26 15:24:49 -04:00
738e39ba92 Fix Dispatcher test 2020-08-26 15:23:47 -04:00
18e8d47167 Move Kitsu class out of API namespace 2020-08-26 15:22:14 -04:00
c429ce64d3 Missing pieces of previous commit 2020-08-25 16:06:00 -04:00
9003c15929 Abort previous requests when search for anime or manga 2020-08-25 16:02:15 -04:00
eb56ab4c4f Misc fixes and tweaks 2020-08-25 15:11:08 -04:00
29a79577d9 Start of pulling library from GraphQL 2020-08-25 13:22:38 -04:00
e890f978db Update History to use GraphQL, resolves #29,#30 2020-08-24 19:17:41 -04:00
e944ddc75c Update profile page to use GraphQL, see #27 2020-08-24 15:20:07 -04:00
778cda6efc Some syncing cleanup 2020-08-24 13:10:43 -04:00
e912c83079 Update some GraphQL queries 2020-08-24 13:09:43 -04:00
78b9146249 Get library entry via GraphQL, see #28 2020-08-24 13:07:47 -04:00
e40a1d028f Fix setup of console commands 2020-08-21 19:26:54 -04:00
edb022be13 Use components instead of duplicating html everywhere 2020-08-21 19:25:27 -04:00
b75a99a145 Fix tests 2020-08-21 13:07:00 -04:00
7aeb74874b Create component system to help cut down on view duplication, see #31 2020-08-21 12:30:01 -04:00
9749c59549 Drastically reduce the amount of junk logging, and remove old logic from Character transformer 2020-08-18 16:59:08 -04:00
5da0ba87a7 Use constants for the API names instead of literals 2020-08-17 21:08:53 -04:00
c749c7c923 Fix sync command 2020-08-17 18:08:58 -04:00
9b4c9ad76f Full character page pulled from GraphQL API, see #27 2020-08-17 16:36:55 -04:00
681a70fd92 Get character details page from GraphQL, still need to do castings section, see #27 2020-08-17 14:01:55 -04:00
67d3b7c1dc Fix manga description page 2020-08-17 11:36:01 -04:00
79aee53524 Add streaming links back to anime description pages, see #27 2020-08-17 10:45:17 -04:00
56f7d5142d Update Kitsu GraphQL schema reference 2020-08-17 10:25:36 -04:00
5f7f4b6bdd Update Kitsu GraphQL Mutations 2020-08-17 10:24:17 -04:00
0c3ff2ef11 Improve error logging 2020-08-17 10:23:32 -04:00
5997ce8a0f Remove some naming redundancies 2020-08-06 09:39:12 -04:00
687831efd5 Add missing change from previous commit 2020-08-05 21:52:36 -04:00
5a65c7b645 Add background check for session validity
This checks when the app is made visible -- like the tab is switched to,
if the current session is still valid. If the session is not still
valid, the page is reloaded so that the session expiration is apparent.

Resolves #25
2020-08-05 21:46:14 -04:00
9dc6643b78 slugs in person urls, refactor AnilistTrait to match KitsuTrait 2020-08-05 20:57:01 -04:00
c7beb76404 Create and delete media items via GraphQL, see #28 2020-08-05 13:30:24 -04:00
c132766486 Move GraphQL queries up a level 2020-08-04 14:25:18 -04:00
9a112dc413 Bump version 2020-08-04 09:30:21 -04:00
1c3216e26a Get manga updates working correctly with GraphQL, see #28 2020-08-04 09:20:28 -04:00
78b195f966 Add some previously missing GraphQL fields 2020-08-03 14:36:14 -04:00
a35bce8a4b Actually fix anime episode incrementing 2020-07-31 20:00:11 -04:00
93faf7d88c Fix json loading overlay for anime updates 2020-07-31 19:32:13 -04:00
a0e7ebd2a0 Increment chapter/episode counts via GraphQL 2020-07-31 19:03:27 -04:00
2b54ab5497 Add GraphQL schema reference for Kitsu 2020-07-31 18:59:16 -04:00
7bfdd74f22 Add GraphQL schema reference for Anilist 2020-07-31 18:58:49 -04:00
4582e2e917 Refactor/streamline View layer 2020-07-31 16:22:32 -04:00
b0c75d989f Groundwork for some upcoming API updates 2020-07-30 15:16:24 -04:00
a3bae9255b Actually fix the character API call, previous commit added authentication to GraphQL calls 2020-07-30 10:02:44 -04:00
3ab34a64d0 Fix issue retrieving some characters 2020-07-30 09:58:36 -04:00
8110f10c3d Skip broken test 2020-07-29 22:08:54 -04:00
7dae2dd6eb Small fixes for anime detail pages 2020-07-29 20:06:59 -04:00
7c0ea492e1 Get Manga details from GraphQL, See #27 2020-07-29 17:51:58 -04:00
9135598649 Fetch anime details by id for the collection 2020-07-29 16:25:57 -04:00
0b0e06af00 Anime detail page cleanup 2020-07-29 15:49:16 -04:00
1ae99d2189 get anime staff from GraphQL, see #27 2020-07-29 14:04:03 -04:00
7275d81468 Re-add characters to anime details page, see #27 2020-07-29 11:00:54 -04:00
dbfdd1c239 Run local phpunit from robo 2020-07-29 11:00:06 -04:00
9eec7123a3 Use GraphQL request for anime detail pages, see #27 2020-07-28 17:46:18 -04:00
710d18a43b Prepare for Kitsu GraphQL 2020-07-28 16:11:13 -04:00
6d66ad1ea4 Merge remote-tracking branch 'origin/develop' 2020-05-19 13:19:20 -04:00
8d87d2fb2b Remove extra titles from cover/list views 2020-05-18 13:52:27 -04:00
61fcffdcbe Make sure reAuthenticate method has optional parameter 2020-05-18 13:47:41 -04:00
057216a21c Make sure re-authenticate gets arguments 2020-05-18 13:32:02 -04:00
abb17844fd Add aria attributes to selected menu items 2020-05-18 12:53:00 -04:00
891d8af469 Remove extra titles from list/cover display 2020-05-18 12:52:32 -04:00
c701999af1 Bug fixes 2020-05-11 09:17:11 -04:00
af0b392e78 Only the command line should be able to get credentials from the cache 2020-05-08 21:34:36 -04:00
2cc85049f3 Refactor KitsuTrait 2020-05-08 19:18:10 -04:00
21a98dc48e Remove APCu as a cache option...it doesn't work with CLI authentication 2020-05-08 19:17:11 -04:00
3ecccb6ad8 Fix settings page subforms (so all the fields show for the cache) 2020-05-08 19:16:04 -04:00
e724f885c8 Simplify caching 2020-05-08 19:15:21 -04:00
43f07dac6c Set up Event-based handling for a few things 2020-05-06 13:16:40 -04:00
7bcff79d6e Fix failing test 2020-05-06 10:12:49 -04:00
f9f868be9d Show more alternate titles on anime detail pages 2020-05-06 09:08:27 -04:00
4a70422b23 Add better re-read messages to manga 2020-05-05 19:12:17 -04:00
d8167ed075 Comment cleanup of sync command 2020-05-04 17:15:50 -04:00
b6c0db7636 Refactor list sync to be easier to follow 2020-05-04 17:13:03 -04:00
ffd7fb8745 Improve rewatched messages 2020-05-04 16:46:27 -04:00
75bd011a2c Various code tweaks 2020-05-01 19:38:45 -04:00
03638991a3 Fix collection episode length/count, display newlines in notes 2020-05-01 19:33:51 -04:00
a7e6b3f198 Make authentication more reliable for list syncing 2020-05-01 17:08:20 -04:00
d5e3dcaff8 Merge remote-tracking branch 'origin/develop' 2020-05-01 11:26:25 -04:00
9108fe066a Tweak anime list view a bit 2020-04-30 15:35:32 -04:00
f810e2573e Collection updates for 'all' tab 2020-04-30 15:33:16 -04:00
a371a334d0 Collection view tweaks 2020-04-30 15:30:52 -04:00
ed72eaef84 Merge remote-tracking branch 'origin/develop' 2020-04-28 12:25:35 -04:00
bcbd76d4a9 Update dependency versions 2020-04-28 12:24:34 -04:00
754cf80c0b Type *Type classes a bit more strictly 2020-04-28 12:24:12 -04:00
ce0935333b Tweak error handling of anime collection db calls 2020-04-28 12:13:35 -04:00
050ff98d2c Add AnimeCollection link to MainMenu, similar to lists 2020-04-28 12:04:42 -04:00
44d2c0e29d Move unusued graphQL queries 2020-04-28 12:03:14 -04:00
c14bf3a8af Merge remote-tracking branch 'origin/develop' 2020-04-24 16:55:34 -04:00
42ffef32fe Don't show episode/chapter 0 in history 2020-04-24 14:18:35 -04:00
1cc5703cd7 Fix some bugs with history view 2020-04-24 14:14:52 -04:00
62be0beae6 Fix history and collection bug 2020-04-23 20:03:55 -04:00
e2e23a290f tweak cleanup migration 2020-04-23 19:40:59 -04:00
541b59bb28 Improve anime collection with multiple media selections 2020-04-23 18:57:22 -04:00
d81fba030c Add migrations for collection improvements 2020-04-23 18:54:54 -04:00
209655adc3 Update dependency to published version 2020-04-23 18:53:51 -04:00
8be0fceb69 Minor tweaks to css and js 2020-04-23 18:51:12 -04:00
8094ff5927 More refactoring of History transformers 2020-04-22 17:53:25 -04:00
b614505499 Add migration to create a link table between anime_sets and media 2020-04-22 17:52:07 -04:00
e17846f4a4 Refactor history transformers 2020-04-22 12:38:59 -04:00
59f2d21a7f Add menu items for history, add manga reading history 2020-04-22 11:39:44 -04:00
0a83184db6 Convert Type constructors to static methods 2020-04-22 07:53:52 -04:00
e3e32b4408 Fix off-by-one errors 2020-04-21 20:37:42 -04:00
c424e3a65a Merge remote-tracking branch 'origin/php74' into develop 2020-04-21 20:13:59 -04:00
2325c8f4ec Fix aggregation of anime watch history items 2020-04-21 20:10:01 -04:00
5a3d9547ae Fix tests 2020-04-21 20:09:37 -04:00
bc529e57e8 Big Work in progress commit 2020-04-21 19:22:56 -04:00
f71a1ee1ae Update outdated interface reference 2020-04-17 13:34:36 -04:00
797e66e520 Temporarily require develop of Query 2020-04-17 13:19:43 -04:00
308a564a2d Merge branch 'php74' of github.com:timw4mail/HummingBirdAnimeClient into php74 2020-04-13 09:20:45 -04:00
174877ec81 More types 2020-04-13 09:20:05 -04:00
83bb85615a Fix merge conflict 2020-04-13 09:17:50 -04:00
570c18a069 Type all the class attributes 2020-04-10 20:01:46 -04:00
462e93292b Add more types 2020-04-10 16:35:01 -04:00
82cd204469 Remove some old frontend files 2020-04-10 15:43:12 -04:00
6d55d4136e Bump version and PHP requirement in headers 2020-04-10 15:39:39 -04:00
546789ce40 Update dependencies to latest versions 2020-04-10 15:38:32 -04:00
97be2e40f8 Merge branch 'php74' of timw4mail/HummingBirdAnimeClient into develop 2020-04-10 15:27:28 -04:00
dd708bb1fa Just combine JS files for modern browsers, no minifying 2020-04-10 15:20:47 -04:00
93a6dbe7d6 Clean up public folder, move JS tools to frontEndSrc folder 2020-04-10 15:07:08 -04:00
fae3314b56 Merge remote-tracking branch 'origin/develop' 2020-04-10 10:17:23 -04:00
dbb61372c6 Fix streaming logo display 2020-04-08 10:08:56 -04:00
0b4c2c81c3 Merge remote-tracking branch 'origin/develop' 2020-04-07 22:07:01 -04:00
0aee62c174 Fix image snapshot test 2020-04-07 22:04:15 -04:00
7fd881c8e9 Lazy load images 2020-04-07 21:53:53 -04:00
9158d01fdf Update snapshot tests for form generation 2020-03-17 15:39:33 -04:00
ae8df3e6d6 Simplify Jenkins build again 2020-03-17 15:15:20 -04:00
f6e00d4336 Downgrade snapshot library to a version that works with PHP 7.3 2020-03-17 15:05:28 -04:00
34b454c175 Attempt to clear dependencies on each step 2020-03-17 11:25:17 -04:00
5ce34200c9 Add PHP 7.3 test section back 2020-03-17 11:21:27 -04:00
1a6a30ef5d Update test snapshots 2020-03-16 15:47:33 -04:00
8faf33c438 Try only PHP 7.4 for tests 2020-03-16 15:25:31 -04:00
6e16632988 Revert "Will tests still work with fewer file priviledges?"
This reverts commit 14db3f1ec9.
2020-03-16 15:23:19 -04:00
14db3f1ec9 Will tests still work with fewer file priviledges? 2020-03-16 15:19:43 -04:00
a4fe28f7b5 Fix some style issues 2020-03-16 15:06:55 -04:00
7c796b3d7b Cleanup some build/quality check stuff 2020-03-13 09:53:31 -04:00
06529d7c92 More test coverage of FormGenerator 2020-03-12 12:47:02 -04:00
42948017a4 Test FormGenerator 2020-03-12 12:32:32 -04:00
055ec80236 Increase code coverage 2020-03-12 12:04:20 -04:00
986ff6de0b Update header comments again 2020-03-12 11:45:11 -04:00
e6a216704c Remove XML codec 2020-03-12 11:44:19 -04:00
beaeb13353 Yet another CI code coverage commit 2020-03-12 11:29:00 -04:00
0ea35cb421 Yet another CI code coverage commit 2020-03-12 11:20:07 -04:00
0feb44a836 Yet another CI code coverage commit 2020-03-12 11:17:27 -04:00
9b33a45189 Yet another CI code coverage commit 2020-03-12 11:09:56 -04:00
e0fa618b4e Yet another CI code coverage commit 2020-03-12 11:08:18 -04:00
7672976f47 Yet another CI code coverage commit 2020-03-12 11:04:22 -04:00
8036104731 Yet another CI code coverage commit 2020-03-12 10:57:19 -04:00
76cb8ca00b Yet another CI code coverage commit 2020-03-12 10:49:32 -04:00
cc3b999bc5 Yet another CI code coverage commit 2020-03-12 10:44:29 -04:00
fb327f0c58 Yet another CI code coverage commit 2020-03-12 10:40:39 -04:00
ee914d048b Yet another CI code coverage commit 2020-03-12 10:36:20 -04:00
f57466d42c A docker image that exists does help 2020-03-12 10:28:21 -04:00
3ce928a67d The correct syntax helps for working CI 2020-03-12 10:26:00 -04:00
622b435337 Another attempt to fix CI 2020-03-12 10:24:21 -04:00
6acee9ca7a Attempt to do code coverage with less memory to begin with 2020-03-12 10:12:28 -04:00
16ecfe3eb5 Does code coverage work with a lower explicit memory limit? 2020-03-12 10:04:05 -04:00
c0fa4cfed2 Try coverage again without setting memory limit 2020-03-12 10:00:20 -04:00
00ef5c3706 Fix test suite 2020-03-12 09:52:45 -04:00
618328a4c1 Move AnimeClient tests 2020-03-11 23:18:31 -04:00
e5ef054f5b Put Ion Namespace back in the codebase directly 2020-03-11 23:04:01 -04:00
d9e81a7cf1 Increase CI PHP memory limit 2020-03-11 22:27:47 -04:00
b334a60486 Skip robo lint, as it fails on test snapshots 2020-03-11 22:21:23 -04:00
17b01f6d48 Make sure git is installed for CI 2020-03-11 22:18:04 -04:00
ef7c1da5f2 Move source code to sub folder so we can re-integrate ion 2020-03-11 22:11:00 -04:00
6718fc78e9 Remove dependency causing issues on PHP < 7.4 2020-03-11 16:34:33 -04:00
5216b60789 Update all the header files again 2020-03-11 16:31:52 -04:00
37c3d6ecf0 Move to Amp/HttpClient from Amp/Artax 2020-03-11 16:26:17 -04:00
eb12f57e7d Attempt to fix Jenkins build, take 2 2020-03-11 15:29:34 -04:00
a03b9be329 Attempt to fix Jenkins build 2020-03-11 15:24:15 -04:00
07de5ff79b Show composer install error for CI, please 2020-03-11 15:16:43 -04:00
8aa94f7c14 Update all the header comments 2020-03-11 15:15:05 -04:00
8842df76be Bump PHP requirement 2020-03-11 15:14:34 -04:00
6047444077 Fix a method of Anilist Model 2020-03-11 15:12:10 -04:00
95e8b7920a Remove php7.2 step in CI 2020-03-11 15:05:27 -04:00
66b13ef7ba Add code coverage to CI 2020-03-11 15:03:04 -04:00
b32968588a Minor view updates 2020-01-15 15:23:55 -05:00
fafd75b791 More error checking 2020-01-15 15:22:38 -05:00
70eb4f11b3 Better id mapping error handling for Anilist 2020-01-15 12:35:37 -05:00
ae70eab9ea Bump copyright year 2020-01-08 15:39:49 -05:00
926179a72d More refactoring/cleanup 2019-12-09 16:17:25 -05:00
143229bea4 Automatically fix some docblocks 2019-12-09 14:41:04 -05:00
3978c4d5cb Update all the docblocks 2019-12-09 14:34:23 -05:00
705d48abad Annotate property types for Types classes 2019-12-09 13:40:54 -05:00
6044a676a6 Various code style tweaks 2019-12-09 13:13:31 -05:00
245e1b4344 Various code cleanup 2019-12-06 15:46:56 -05:00
ec9edff2f3 Add linting check to CI to help catch version-incompatible code 2019-12-06 10:05:09 -05:00
3fa5b7ab88 Downgrade snapshot library so that more PHP versions work for tests 2019-12-06 09:25:04 -05:00
ceb8159dae Remove composer.lock for each test run to make sure dependencies are correctly installed 2019-12-06 09:19:47 -05:00
8b677ab7a7 Update header comments 2019-12-06 09:16:35 -05:00
b4b5c63d65 Tweak tests for new version of PHPUnit 2019-12-06 09:15:49 -05:00
347674f9e5 Update dependencies 2019-12-05 16:59:24 -05:00
d435a17ec8 Add PHP 7.4 tests 2019-12-05 11:20:06 -05:00
fde9b05bdf Bump PHP version requirement 2019-12-03 15:17:25 -05:00
7d9d2e8990 Merge remote-tracking branch 'origin/develop' 2019-12-02 15:30:04 -05:00
995690a341 Update clear thumbnails script to work with lots of files 2019-12-02 15:29:24 -05:00
808b704383 Merge remote-tracking branch 'origin/develop' 2019-10-08 20:25:24 -04:00
cd835055ec Catch errors when mapping MAL ids on sync 2019-10-08 19:59:47 -04:00
117427ced0 Misc bugfixes, especially for Anime without a MAL id. 2019-10-07 20:10:27 -04:00
aebb349543 Update CI stuff 2019-08-16 14:40:15 -04:00
37f7616ef4 Merge remote-tracking branch 'origin/develop' 2019-08-16 10:39:50 -04:00
28d4ce9e86 Remove php 7.4 version in travis config that doesn't exist yet 2019-08-16 10:35:59 -04:00
0e893f06ba Minor code cleanup, add newer php version for travis tests 2019-08-16 10:31:31 -04:00
58bb1ab0ba Update base request builder to use the correct user agent 2019-08-10 10:42:02 -04:00
46041ccfc6 Add first GraphQL files for Kitsu for future implementation 2019-08-10 10:10:09 -04:00
625edf5d0c Improve 404 checks for detail pages 2019-08-10 10:09:07 -04:00
4edfd9f62c Update detail pages to use one column for text 2019-08-10 10:07:28 -04:00
e5baccbf56 Simplify _.show and _.hide useage 2019-07-15 16:05:29 -04:00
bfd5ff32c1 Merge remote-tracking branch 'origin/develop' 2019-07-15 14:31:17 -04:00
e1fd639ba9 Fix scroll to top on list item update 2019-07-12 23:12:05 -04:00
4e88c27cfb Remove now unused css file 2019-07-12 16:27:39 -04:00
0153271a62 Add 'automatic' dark theme, based on browser 'prefers-color-scheme: dark' media query 2019-07-12 15:56:24 -04:00
7a529619ed Update js dependencies 2019-07-12 13:33:40 -04:00
a6020df023 Merge remote-tracking branch 'origin/develop' 2019-07-12 13:28:37 -04:00
cda711607a Clean up commands a little bit 2019-07-11 19:03:35 -04:00
c5bb555695 Show fewer sync errors by filtering common data disparity issues 2019-07-11 16:38:21 -04:00
ea5eb21941 Fix syncing manga to anilist when you have to create a new list item 2019-07-11 15:24:34 -04:00
038e61bf37 Make Anilist missing username error more reliable, allow editing anilist username in settings panel 2019-07-11 10:28:09 -04:00
bce1afa546 Collection "All Tab", and filtering. Resolves #6, #7 2019-07-10 13:32:05 -04:00
4502c2f183 No more genre-related database errors, and other collection improvements 2019-07-10 10:20:37 -04:00
b070282899 Merge remote-tracking branch 'origin/develop' 2019-05-08 16:09:29 -04:00
aa6965e98f Tweak display of descriptions on detail pages 2019-05-08 16:08:51 -04:00
b2c8bff967 Merge remote-tracking branch 'origin/develop' 2019-05-08 14:18:49 -04:00
ea2a368100 Use larger cover images for edit forms 2019-05-08 14:18:18 -04:00
523fcbd0d9 Edit form style tweaks 2019-05-08 14:17:57 -04:00
003492a964 Merge remote-tracking branch 'origin/develop' 2019-05-08 13:19:32 -04:00
e98699acbc Fix thumbnail generation command 2019-05-08 13:19:03 -04:00
eb31096bff Merge branch 'develop' of timw4mail/HummingBirdAnimeClient into master 2019-05-08 12:40:32 -04:00
d1a0147ae2 Button and Select style tweaks 2019-05-08 11:14:11 -04:00
6c81ddbfd4 Add polyfill for older browsers, so Opera 12 works 2019-05-08 08:57:15 -04:00
2fdca56501 Update js sourcemaps 2019-05-08 08:56:26 -04:00
47d6314178 Use static closures in bootstrap 2019-05-08 08:55:58 -04:00
e73326fd6c Update css/js dependencies 2019-05-08 08:53:34 -04:00
251bbadd96 Style tweaks. Fixes #16. 2019-05-08 08:50:57 -04:00
6ee198c742 Fix some edge cases 2019-04-01 16:17:40 -04:00
dfdc6b7356 Remove XML tests 2019-03-12 09:47:59 -04:00
94b15f455c Remove XML codec class 2019-03-12 09:43:17 -04:00
f6278a1304 Consistent spacing around auth checks 2019-01-29 16:01:31 -05:00
04c5b135a7 Add a per-controller-method check for authorization for private routes 2019-01-29 15:12:31 -05:00
8a8ea0b470 Cleanup redundant methods in Collection model 2019-01-28 14:31:48 -05:00
4e2437f2bc Fix error on attempt to insert a duplicate series 2019-01-22 10:21:58 -05:00
27c7f08d7f Hide missing table error on noninitialized collection, see #20 2019-01-08 15:52:53 -05:00
b6f12ff2f6 Update phinx.yml file for new version of Phinx, see #20 2019-01-07 14:31:17 -05:00
317d8fd29b Cleanup database logic a bit 2019-01-07 14:29:15 -05:00
b66a35843d Small code cleanup 2019-01-07 09:08:00 -05:00
ac382a96a8 Simplify/clean up some base classes 2018-12-21 15:52:34 -05:00
87d0960424 Merge branch 'develop' of timw4mail/HummingBirdAnimeClient into master 2018-12-13 14:51:01 -05:00
be16ceecb2 Make syncing slightly more robust 2018-12-12 15:31:59 -05:00
105b0f52ca Use the same API client instance across the codebase 2018-12-07 10:24:42 -05:00
63a50f7ed8 Don't show media tabs with no media on character page, make stats on user page more resiliant 2018-12-07 10:22:16 -05:00
4b9f97f49e Remove default API client timeouts, fix time on anime calculation 2018-12-06 16:21:02 -05:00
8272cc7240 Remove CSS sourcemaps...because they're pointless 2018-12-06 13:44:31 -05:00
538201ef6f Add dark theme with setting toggle 2018-12-06 13:04:54 -05:00
5e9780aad7 Update misspelled method, somehow resolves #19 2018-11-29 11:46:06 -05:00
f3265484da Some API client cleanup 2018-11-29 11:00:50 -05:00
4c3f987b85 Fix error in list sync 2018-11-27 15:37:16 -05:00
5e3fb46159 Update cache dependency 2018-11-27 14:57:27 -05:00
2c73e721d0 Misc code cleanup 2018-11-09 10:38:35 -05:00
431f6e7d21 Purge the few inline styles 2018-11-08 14:18:24 -05:00
c0e16c6d07 Remove data transformation from media detail pages, and into the proper transformers 2018-11-08 12:15:30 -05:00
9c0b1e73ef Move data transformation out of controllers, and into transformers 2018-11-08 11:36:42 -05:00
05842baccb Fix generic user page route, minor code cleanup 2018-11-07 14:29:21 -05:00
7f6b0178a8 Merge remote-tracking branch 'origin/master' into develop 2018-11-05 13:25:42 -05:00
8b938add27 Fix collection query 2018-11-05 13:25:18 -05:00
8672112bdc Merge branch 'develop' of timw4mail/HummingBirdAnimeClient into master 2018-11-05 13:15:58 -05:00
033ea46754 More styling tweaks 2018-11-05 11:22:35 -05:00
24f80bc18c Make tables responsive 2018-11-05 11:04:19 -05:00
4b75987f21 Fix broken snapshot test 2018-11-05 10:42:51 -05:00
ca487901c2 Sort streaming links by service 2018-11-05 10:40:29 -05:00
e195987436 Some visual tweaks 2018-11-05 09:56:38 -05:00
350dae0109 Responsive updates for smaller screen sizes 2018-11-05 09:47:05 -05:00
d514c319c0 Update picture helper, move anilist oauth calls to the settings controller 2018-11-02 12:58:19 -04:00
4ace9b6806 Make all the css classes and ids kebob case 2018-11-02 10:48:20 -04:00
64478d4507 Update controller test 2018-11-01 22:16:45 -04:00
f314538972 Various refactoring, better webp image handling 2018-11-01 22:15:20 -04:00
155650961b Make Controllers more specialized 2018-11-01 22:12:41 -04:00
b3366131b8 Lots of visual updates 2018-11-01 22:01:09 -04:00
040b7f3fdc More page style tweaks 2018-10-30 13:05:49 -04:00
ef1e435c6b Add tabs to character page sections 2018-10-30 11:42:32 -04:00
a9c3a44f37 Fix css for character images on user page 2018-10-30 09:43:54 -04:00
3842df13db Small code consistency update 2018-10-29 15:48:54 -04:00
be2f7708ad Add staff section on Manga detail pages 2018-10-29 15:17:48 -04:00
29a4114e8c Fix staff section on Anime detail pages, center unusually sized images instead of stretching them 2018-10-29 14:43:06 -04:00
1690d8c1e0 Attempt to fix tests again 2018-10-29 10:07:20 -04:00
e84cbe1bb2 Update test snapshots 2018-10-29 09:41:50 -04:00
d0af6fd9e8 Update JsonAPI helper to better handle input data without mangling 2018-10-29 09:39:56 -04:00
bcc7815ae6 Ugly Progress Commit
* Update Person pages to have series organized by character for Voice
Acting
* Miscellaneous style updates
* Add placeholder images for items missing images
2018-10-26 13:08:45 -04:00
5d87bd044c Refactor some silly switches 2018-10-19 10:40:11 -04:00
e2b4fae83b Fix broken tests 2018-10-19 10:39:38 -04:00
019fff5d62 Miscellaneous page improvements, including additional data and sorting 2018-10-19 09:30:27 -04:00
cf1c495f90 Remove need for www subdomain for streaming service mapping 2018-10-17 14:33:16 -04:00
cee5a28816 Replace switch statement with array mapping 2018-10-17 14:20:07 -04:00
83a6629f03 Fix tests, and category list for Manga detail page 2018-10-16 14:32:52 -04:00
5810405f12 Remove a reference to genres from an older version of the Kitsu API 2018-10-16 14:22:47 -04:00
7b765c6d0b Account for missing genres in anime collection 2018-10-11 16:40:51 -04:00
a9acf23a15 Update docs a bit 2018-10-11 13:25:53 -04:00
b14a827715 Update shell script for Jenkins 2018-10-11 13:06:58 -04:00
1cd8386559 Closer to working Jenkins 2018-10-11 12:50:29 -04:00
1fa11cd50d Try again, for Jenkins 2018-10-11 12:22:44 -04:00
8a7223593d Maybe third time is a charm for Jenkins? 2018-10-11 12:08:57 -04:00
ccc9ad0c14 Attempt2 with Jenkins 2018-10-11 11:54:27 -04:00
509db151e7 Let's do CI with Jenkins 2018-10-11 11:11:24 -04:00
0bbc4fe4fb Default to secure (https) urls 2018-10-11 09:53:14 -04:00
86c311dddf Add console command to re-generate list thumbnails 2018-10-10 16:04:58 -04:00
eaf3554611 Attempt to fix ssl detection 2018-10-10 15:58:28 -04:00
99aaf0303b Fix broken url generator test 2018-10-10 14:26:44 -04:00
6d1df75889 Always set the url protocol for the url generator 2018-10-10 14:21:46 -04:00
9c8e396c9a Fix javascript minification 2018-10-10 12:57:11 -04:00
c9ec11c2df Fix tests 2018-10-09 18:26:42 -04:00
587d5fa14e Update config.toml.example file 2018-10-09 18:21:06 -04:00
229703a6a6 Update README 2018-10-09 18:15:03 -04:00
5b8f0c4a9e Full Anilist settings page OAuth flow, ability to run app without manually editing config files. See #7. Resolves #5 2018-10-09 18:10:20 -04:00
41d71dac0c Cleanup styles of settings page, cleanup syncing command a bit 2018-10-09 10:11:42 -04:00
6dfa66dbde Fix issue with cache settings 2018-10-08 16:47:40 -04:00
324abc0f61 More settings, now with tabs 2018-10-08 16:38:08 -04:00
3c0fd79195 Settings control panel saves to admin-override.toml in the app/config directory, resolves #7 2018-10-08 15:45:46 -04:00
0d30f57e83 Update gitignore 2018-10-08 15:44:03 -04:00
247a9d0e5b More webp images, fix login 2018-10-05 22:36:54 -04:00
d6800dbc46 Ugly Progress Commit
* Cache and resize images - not just cache them
* Convert to webp on cache
* Show webp images if available
* Settings Form Generation (doesn't yet save)
2018-10-05 21:32:15 -04:00
ae283cd898 Add command to check Kitsu's MAL id mappings 2018-10-05 14:40:30 -04:00
a8f898822a Update code to use simpler config 2018-10-05 14:32:05 -04:00
da936b325e Merge config.toml and route_config.toml 2018-10-05 14:27:07 -04:00
8b3ce0f079 Fix some api mapping issues for #5 2018-10-01 13:03:48 -04:00
c9ed90acb4 Update header comments to version 4.1 2018-10-01 11:35:51 -04:00
17a9539e94 More work on #5 2018-10-01 10:50:22 -04:00
6f717e6ab7 Fix tests 2018-09-27 16:48:12 -04:00
0f31a5e10a Ugly progress commit 2018-09-27 16:45:12 -04:00
e0376c78d1 Fix tests 2018-09-26 22:43:04 -04:00
a6c253b969 Lots of Anilist integration, see #5 2018-09-26 22:31:04 -04:00
5a607db93e Rebuild scripts and css 2018-09-20 16:12:28 -04:00
e7dc1e8e53 Anilist CRUD operations for Anime! See #5 2018-09-20 16:08:46 -04:00
38a5b78295 Update test snapshots 2018-09-20 11:53:12 -04:00
034213fccc Progress with simultaneous updates to Anilist for Anime 2018-09-20 10:41:28 -04:00
020f561773 JS style updates 2018-09-19 14:11:35 -04:00
2fc26bf4c6 Let's do ES modules for browsers that support them 2018-09-14 11:56:48 -04:00
09530efefd Optimize streaming service logos 2018-09-12 14:20:51 -04:00
0624c3be67 Check config object shape on page load 2018-08-24 14:37:53 -04:00
a71fb185bd Add Config 'Type', to keep config settings somewhat in check 2018-08-24 14:36:58 -04:00
98ae142757 Cleanup config a bit 2018-08-24 14:23:01 -04:00
cd150d7fef Fix stupid type error 2018-08-22 13:51:58 -04:00
3bca049cd8 Update file header comments 2018-08-22 13:48:27 -04:00
95b06a7e7e Eradicate MAL integration 2018-08-22 13:43:04 -04:00
81f02ad622 Cleanup javascript into fewer files, add show/description link to search pages 2018-08-22 12:54:06 -04:00
64d3c241f6 Update service worker so it only caches images 2018-08-21 17:11:03 -04:00
4a91a5cb5d Anime Collection improvements
* Allow editing title and alternate title
* Show list of genres on list view of collection
2018-08-21 17:09:42 -04:00
226f0ced83 Getting started with some service workers 2018-08-20 16:24:33 -04:00
bc2122dd98 Fix test failure 2018-08-20 13:41:25 -04:00
2a2ff87b3b Various cleanup, some work on #7 2018-08-20 13:01:16 -04:00
7c0d02758b Remove php js minifier script, in favor of commited js files 2018-08-20 12:58:56 -04:00
e6761807b8 Add basic check for folder permissions for quicker troubleshooting 2018-08-16 12:10:24 -04:00
bfb5d6323d More progress on #5 2018-08-15 16:19:07 -04:00
b3a3e19146 Making API requests to Anilist, see #5 2018-08-15 14:05:28 -04:00
b5f8413ceb More prep for Anilist integration 2018-08-15 08:51:37 -04:00
b2554378e7 Merge remote-tracking branch 'origin/develop' 2018-08-14 11:37:43 -04:00
1fa5cce5ae Remove lines and shading from add forms 2018-08-14 11:36:26 -04:00
ea31131e0f Adjust layout of edit pages 2018-08-13 15:13:20 -04:00
675f8ed9b2 Update test snapshots 2018-08-10 20:11:22 -04:00
0dcf25e16c More refactoring work, some groundwork for Anilist integration 2018-08-10 20:10:19 -04:00
dd46e292c4 Update some styles 2018-08-10 20:09:28 -04:00
e9888c762e Fix test snapshots 2018-08-10 11:01:30 -04:00
49295148d1 Update some types
* Remove empty values from types for serialization, so that empty values
are not sent with API requests
* Allow use of explicit setters for more complex types
2018-08-09 11:34:02 -04:00
d1421d2eb2 Better error handling for incrementing watched count on Anime list 2018-08-09 11:31:15 -04:00
69c2482fc1 Remove references to MAL syncing, resolves #4 2018-08-09 11:16:44 -04:00
571bbf7595 More tabs for collections, see issue #2 2018-08-09 11:14:57 -04:00
e01a96b8fb First go at tabs for collection 2018-08-08 17:04:35 -04:00
2c7d866677 Fix PHP 7.1 test 2018-08-08 13:08:10 -04:00
be2b387391 More refactoring, fix snapshot tests 2018-08-08 13:05:38 -04:00
06c55a2094 Fix tests 2018-08-08 11:18:57 -04:00
9a7084078f Some minor code cleanliness refactoring 2018-08-08 10:12:45 -04:00
f71e9dbe4d Merge remote-tracking branch 'origin/master' into develop 2018-06-15 08:47:16 -04:00
cecca5f9f0 Give a better error message on failing to parse an XML API response 2018-06-15 08:46:28 -04:00
79be0ebb34 Use more efficient method of combining large sets of data from Kitsu 2018-04-11 09:26:14 -04:00
c7a77a2eb5 Merge branch 'master' of timw4mail/HummingBirdAnimeClient into develop 2018-04-05 23:03:43 -04:00
c0c72e40e4 Add more missing streaming logos 2018-04-05 23:00:58 -04:00
01bf7144c2 Merge remote-tracking branch 'origin/develop' 2018-04-05 21:56:02 -04:00
f5f59b8382 Update wiki and CI links due to move from Gitlab 2018-04-05 21:20:43 -04:00
75a5727a2e Add Hidive to streaming service mapping 2018-04-05 08:56:01 -04:00
4f0c3f7984 Add Hidive to streaming service mapping 2018-04-05 08:39:49 -04:00
83e0310ca7 Merge remote-tracking branch 'origin/develop' 2018-02-02 09:53:22 -05:00
9cac51bd82 Miscellaneous style updates 2018-02-02 09:50:58 -05:00
a434c032a2 Minor refactor of Commands 2018-01-31 15:44:48 -05:00
ad0154f431 Merge remote-tracking branch 'origin/develop' 2018-01-31 11:36:54 -05:00
080b112608 Update test snapshot 2018-01-31 11:00:10 -05:00
92ad051f6a Add trailer videos to anime detail pages 2018-01-31 10:55:20 -05:00
da8f4acb29 Use template literals instead of mustache templates 2018-01-30 16:57:13 -05:00
43ab033aec Merge remote-tracking branch 'origin/develop' 2018-01-30 14:28:07 -05:00
83f9d14630 Fix issue with anime detail pages 2018-01-30 14:02:28 -05:00
634335187e Merge remote-tracking branch 'origin/develop' 2018-01-25 19:31:34 -05:00
f06ba6e3cd Lots of style fixes and minor logic tweaks 2018-01-18 16:21:45 -05:00
675b1ef2f1 Update README for requirements 2018-01-16 15:05:17 -05:00
a46a85bf71 Remove php 7.0 test 2018-01-16 15:04:25 -05:00
3a739e3920 Make character layout more closely match anime/manga pages 2018-01-16 14:58:30 -05:00
231babe218 Code style improvements 2018-01-16 14:58:07 -05:00
4532bd1865 Remove strict types from Artax client implementation 2018-01-15 14:49:17 -05:00
7b9adbf52e Update copyright year 2018-01-15 14:43:15 -05:00
f607403111 Merge branch 'master' into 'develop'
Correct index of id for kitsu sync

See merge request timw4mail/HummingBirdAnimeClient!22
2018-01-11 09:49:43 -05:00
34996f009b Correct index of id for kitsu sync 2018-01-10 16:43:49 -05:00
39031ccf3e Simplify syncing script 2018-01-10 16:34:25 -05:00
4451544389 Merge remote-tracking branch 'origin/master' into develop 2018-01-10 16:28:37 -05:00
5aa750cde7 Fix some documentation generation issues 2018-01-10 16:24:00 -05:00
11068029e8 Handle syncing errors more consistently 2018-01-10 16:18:06 -05:00
e75c52d9dc Fix bug with sync, remove some code duplication 2017-12-13 11:38:21 -05:00
09f9aa2069 Merge branch 'master' into 'develop'
Merge branch 'develop' into 'master'

See merge request timw4mail/HummingBirdAnimeClient!21
2017-12-11 13:24:30 -05:00
bbe2fd0a5d Update Artax, and update other code to work with new version 2017-12-08 22:32:00 -05:00
2ad0a24483 Miscellaneous code style changes 2017-12-06 14:40:13 -05:00
0c52831ec6 Merge branch 'develop' into 'master'
Update master from develop

See merge request timw4mail/HummingBirdAnimeClient!20
2017-12-06 12:36:23 -05:00
55fca3f92f Correct some errors in creating and deleting anime collection items 2017-12-06 12:30:12 -05:00
034174418b Update dependencies 2017-12-06 11:48:15 -05:00
e8f53542e8 Make sure to add git... 2017-12-06 11:20:12 -05:00
ae52f8737a Don't try to run update, maybe? 2017-12-06 11:16:47 -05:00
888dbe7187 Another attempt to fix gitlab ci builds 2017-12-06 11:12:51 -05:00
37f1391a07 Try, try, again 2017-12-06 11:10:50 -05:00
8b8ece3dad Attempt to fix gitlab ci build 2017-12-06 11:07:23 -05:00
015b3d0f34 Attempt to test PHP 7.2 2017-12-06 10:58:26 -05:00
ee57f72ca6 Merge remote-tracking branch 'origin/master' into develop 2017-12-04 16:07:46 -05:00
9497f5c3df Fix MAL sync issue for anime 2017-12-04 16:06:27 -05:00
63fe8684c4 Fix js minification url 2017-12-04 15:57:13 -05:00
a2fbe471e3 Attempt to fix builds 3 2017-10-19 18:38:56 -04:00
c6f2981d63 Attempt to fix builds 2 2017-10-19 18:34:11 -04:00
410ff4fcc1 Attempt to fix builds 2017-10-19 18:31:28 -04:00
7e8e9a3141 Merge branch 'master' into develop 2017-10-18 20:00:00 -04:00
98c24d4704 Fix about section of user page 2017-10-18 19:59:29 -04:00
c9533db6a1 Merge branch 'master' into develop 2017-10-18 19:42:38 -04:00
a4555ca908 Merge branch 'master' of git.timshomepage.net:timw4mail/HummingBirdAnimeClient 2017-10-18 19:42:01 -04:00
45e14a7503 Add proper logging to console commands 2017-10-18 19:28:57 -04:00
5eaa33ba82 Fix an issue with Kitsu <-> MAL sync 2017-10-18 19:28:22 -04:00
38eee85752 Revert former change so that kitsu anime lists are properly pulled for sync 2017-09-15 16:51:47 -04:00
2b9adb0395 Adding missing method for manga collection 2017-09-15 15:05:35 -04:00
5fb042a773 Better handle empty lists on sync, resolves #29 2017-09-15 15:04:57 -04:00
04ec5b2fd6 Tweak handling of empty list sections 2017-09-14 17:33:24 -04:00
b0ee397994 Fix an issue fetching anime for the add item call 2017-09-14 17:32:40 -04:00
200ff1339c Rough start of Manga collection...need to set up proper structure for manga collection items 2017-09-14 16:18:13 -04:00
a8e2049d08 Refactor a bit to prepare for manga collection 2017-09-14 15:32:53 -04:00
07152cc3be Remove PHP 7.2 test that doesn't work 2017-09-12 12:52:56 -04:00
89816dc062 Test with php 7.2 2017-09-12 12:38:26 -04:00
3c4e34f1ed Fix tests broken by api change fixes 2017-09-12 12:33:57 -04:00
372b616101 Update some api calls based on api changes 2017-09-12 12:18:31 -04:00
d93d22f7df Add overlay during update request on list pages, resolves #31 2017-07-12 16:40:56 -04:00
6493f33faf Attempt to re-authenticate when access token expires 2017-06-19 15:31:24 -04:00
e66a9f885a Some minor refactoring 2017-06-19 13:49:28 -04:00
fa74e59854 Update htaccess so images can load 2017-04-28 13:20:59 -04:00
a15a97370b Update readme with another folder that needs to be writable 2017-04-28 13:20:34 -04:00
3fd7c84774 Further filter titles, showing only the canonical title if it is really long 2017-04-26 10:09:14 -04:00
428a77b93d Merge branch 'develop' into 'master'
Merge develop into master

See merge request !18
2017-04-24 09:28:40 -04:00
8e8ee81397 Kitsu <-> MAL manga list item comparison, resolves #18 2017-04-19 16:48:53 -04:00
cf12dfee76 kitsu <-> mal comparison for anime, see #18 2017-04-19 16:15:39 -04:00
d2fc955260 Miscellaneous code cleanup 2017-04-17 16:13:36 -04:00
4d991629b1 More main menu tweaking 2017-04-17 14:49:33 -04:00
6111dfe6f9 Tweak the main menu a bit 2017-04-17 12:45:29 -04:00
921d594931 Update dependencies, and set a more locked-down content security policy 2017-04-13 15:08:28 -04:00
ac13d57634 Use proxy and cached images on user info page 2017-04-13 14:25:39 -04:00
42d36ff4bb Update manga transformer tests 2017-04-13 11:54:58 -04:00
28da32f2ac Get images from proxy or cache for Manga views, and Add views 2017-04-13 11:44:03 -04:00
08aff2ffe8 All anime images now pull from proxy or cache 2017-04-13 11:26:28 -04:00
8b43dee20f Css tweaks, and start caching kitsu images 2017-04-13 11:15:16 -04:00
0d2cde37a0 Update .gitignore 2017-04-11 13:28:05 -04:00
5cca3cf335 Fix changing a list item's status with no score or progress on MAL 2017-04-11 09:28:07 -04:00
679560e185 Fix various edge cases 2017-04-10 15:31:35 -04:00
d157f097d1 Use a more appropriate function for substring filtering 2017-04-07 16:58:08 -04:00
ec6f9b1189 Tweak handling of alternate titles, to ensure the +1 button is always usable 2017-04-07 16:44:27 -04:00
7825e46321 Replace 0 with - 2017-04-07 13:57:14 -04:00
6436ca2e9c Update doc generation 2017-04-06 21:27:47 -04:00
81a1a927b1 Make sure Cast heading only shows up if there are actual cast entries 2017-04-06 21:27:03 -04:00
b210954874 Make sure rating parameter sent to Kitsu is greater than 0 2017-04-06 14:53:38 -04:00
472be3c4ed Use snapshots library with tests to simplify testcases 2017-04-06 11:59:53 -04:00
edc6e6227e Simplify css and javascript minification 2017-04-06 11:45:25 -04:00
32b3617fed Update postcss 2017-04-06 10:01:09 -04:00
45bf1e1136 Add a better API timeout message emoticon 2017-04-05 13:08:16 -04:00
fb3805b789 Add staff to character pages 2017-04-05 13:02:48 -04:00
b861db5d1f Catch API timeouts 2017-04-05 13:01:51 -04:00
e49ed606f5 Merge remote-tracking branch 'origin/develop' 2017-04-03 16:53:25 -04:00
8172d1a593 Remove some dead code 2017-04-03 16:53:04 -04:00
a413d7d9ca Fix collection images, resolves #26 2017-04-03 16:49:40 -04:00
8ceec846a5 Fix creating missing Kitsu items 2017-04-03 15:46:16 -04:00
d2d48905d7 More basic tests, see #16 2017-04-03 14:46:29 -04:00
7d7ae73f5e Merge remote-tracking branch 'origin/develop' 2017-03-31 17:02:26 -04:00
82c8d36144 small tweak to user page 2017-03-31 17:01:53 -04:00
c50b1da53b Details and user page updates, resolves #27 2017-03-31 16:36:22 -04:00
ddd30fc713 Add favorite characters to user page, see #27 2017-03-31 14:15:29 -04:00
1e28a1795d Update detail pages 2017-03-31 13:37:53 -04:00
b2300f4cfb Remove duplicated function 2017-03-30 16:57:58 -04:00
92fe6b7146 Update header comments 2017-03-30 16:49:48 -04:00
fa4ee22100 Allow over-riding the default lists in the user config 2017-03-30 16:47:02 -04:00
5ae6864a3f Fix config typo 2017-03-30 16:18:59 -04:00
fb567e85e9 Simplify routing code a bit 2017-03-30 16:16:40 -04:00
2d33663318 Add rereading info to manga list 2017-03-30 14:50:25 -04:00
528d3584b8 Make sure rating is only updated if it is numeric 2017-03-29 16:09:22 -04:00
69b4d0c88b More tests 2017-03-29 15:14:30 -04:00
e79021da29 Add and delete manga simulaneously from kitsu and mal 2017-03-29 14:25:03 -04:00
79980683d1 Allow manga +1 button to update both kitsu and mal 2017-03-29 14:00:57 -04:00
ec7e0cc93b Fix tests 2017-03-29 13:42:40 -04:00
15b26d8e39 Simultaneously update kitsu and MAL manga list item 2017-03-29 13:29:03 -04:00
377e102650 Create missing manga items on kitsu and mal with sync command 2017-03-29 12:32:36 -04:00
4a9e0f0293 Update sync lists command to create Kitsu items that are missing compared to MAL 2017-03-28 16:52:27 -04:00
0b18c06058 Minor api model refactoring 2017-03-28 14:36:23 -04:00
8d289f9eb5 Reorgnize order of Kitsu model methods 2017-03-28 14:34:33 -04:00
394c1241fb Update dependencies, use ParallelAPIRequest 2017-03-28 11:01:38 -04:00
0c4c0a436c Merge branch 'develop' into 'master'
Develop

See merge request !17
2017-03-28 10:43:51 -04:00
b8b5beeae1 Code cleanup and fix 'On Hold' title on all section of anime list 2017-03-27 10:09:45 -04:00
be0baac962 List characters on manga pages 2017-03-24 10:59:07 -04:00
cf0db8b9fa Update all the page titles 2017-03-24 09:58:27 -04:00
dd90dd541c Fix title of anime pages 2017-03-24 09:10:30 -04:00
cdd3878e55 Show custom 404 pages for missing anime and characters 2017-03-24 09:08:39 -04:00
51c26e6b30 Remove code coverage ignore annotations 2017-03-24 08:49:39 -04:00
0c2cc0e32b Get rid of whoops 2017-03-23 11:21:13 -04:00
828b7a2154 Fix 'all' view with missing sections 2017-03-22 16:53:46 -04:00
be4f54b99d Merge branch 'master' into 'develop'
Master

Closes #24

See merge request !16
2017-03-22 13:48:01 -04:00
25e57eb493 Merge branch 'develop' into 'master'
Update favicon with blue version, resolves #24

See merge request !15
2017-03-22 13:17:27 -04:00
707a36fe53 Update favicon with blue version, resolves #24 2017-03-22 13:12:29 -04:00
cd58e083bf Merge branch 'master' of git.timshomepage.net:timw4mail/HummingBirdAnimeClient 2017-03-22 12:49:18 -04:00
a259ea7b18 Update streaming link handling, and add daisuki and viewster 2017-03-22 12:29:07 -04:00
74602f60fa Fix syntax error, prime manga cache too. See #19 2017-03-22 12:28:19 -04:00
53b8ce44bf Fix php 7.1 build? 2017-03-22 11:43:20 -04:00
e99205be54 Add command to prime cache, see #19 2017-03-22 11:41:25 -04:00
b52d301b2a Fix issue with updating anime item 2017-03-22 11:15:40 -04:00
52bd2773c0 Remove unused mappings 2017-03-22 11:14:59 -04:00
c9768855a5 All anime api calls are now using paginated requests, see #23 2017-03-22 11:13:50 -04:00
1a45e57b7c Miscellaneous code cleanup 2017-03-20 19:08:33 -04:00
cd242596bc Move link to user profile page 2017-03-20 13:16:01 -04:00
bc6854a8e5 Show characters on anime details page 2017-03-20 13:14:01 -04:00
0d4b26e493 Minor model refactoring 2017-03-14 14:28:08 -04:00
0e6a1b6591 More work on user profile page 2017-03-10 12:50:48 -05:00
84f0a27d86 use readable cache keys 2017-03-10 12:50:29 -05:00
8645926006 More work on profile page 2017-03-08 16:21:01 -05:00
960537f8e0 Fix tests and start on profile page 2017-03-08 13:46:50 -05:00
5d9b7e9e63 Add basic character pages 2017-03-08 12:55:49 -05:00
6bf107e1ad Update all the header comments with the correct repository url 2017-03-07 20:53:58 -05:00
fa7651faf9 Fix issues with sync-lists command, add more docblocks 2017-03-07 20:49:31 -05:00
8b2cd7dd1e Fix 'All' section on Manga page 2017-03-07 18:41:51 -05:00
5d2dac5b99 Add back 'All' menu item for anime 2017-03-07 17:51:08 -05:00
df7102d13d Update gitignore, and make sure cache directory for js minifier exists 2017-03-07 17:48:35 -05:00
82b596ec3c Make sure to actually add streaming logos to repo 2017-03-07 15:22:45 -05:00
497216cffa Make sure mapping is accurately named
\!
2017-03-03 11:51:53 -05:00
969ff75078 Update README 2017-03-03 11:33:42 -05:00
f932a80e58 Minor refactor of bootstrap setup 2017-03-03 11:33:32 -05:00
ae6b1cb209 Move AnimeWatchingStatus and MangaReadingStatus enums to the same namespace 2017-03-02 11:12:19 -05:00
d0a236e7ee Remove accidentially created Java file 2017-03-01 22:11:37 -05:00
ed01b28c0d Rework the rest of the mappings 2017-03-01 22:07:51 -05:00
52e1d1822a Update Manga mappings and enums 2017-03-01 21:52:30 -05:00
5e9b3db1f2 Add new mapping class for Anime watching statuses 2017-03-01 20:51:40 -05:00
e0e1b59777 Merge branch 'develop' into 'master'
Develop

See merge request !14
2017-02-28 16:58:53 -05:00
443ffaa132 Rename on packagist 2017-02-28 16:47:39 -05:00
4b0226838c Attempt to fix travis ci after switch to phpdbg 2017-02-28 14:24:32 -05:00
79ce8c7790 Actually install dev dependencies for gitlab ci 2017-02-28 13:52:39 -05:00
0fa4a3c963 Try, try, again 2017-02-28 13:44:41 -05:00
14967e9ad0 Add phpunit as a dev dependency 2017-02-28 13:39:49 -05:00
b4573296d8 Properly setup test coverage 2017-02-28 13:21:37 -05:00
0127d65dfc Simplify gitlab ci setup 2017-02-28 13:17:06 -05:00
14e8fa9f03 More PHPStan fixes 2017-02-22 15:08:29 -05:00
adc331d60d PHPStan fixes 2017-02-22 14:46:35 -05:00
65227e82ac Move some README info to the wiki 2017-02-21 15:56:19 -05:00
56ae9ed80e Update method references in Manga controller 2017-02-21 15:37:29 -05:00
f3d9af311e Update method references in Collection controller 2017-02-21 15:36:34 -05:00
136b7dab66 Simplify database config example 2017-02-21 14:56:10 -05:00
cd7a836db0 Remove old/unused config options from example file 2017-02-21 14:41:59 -05:00
e87e5cb47c Make sure anime detail pages don't distort images 2017-02-21 12:24:34 -05:00
7122085590 Will teh Gitlab build be triggered? 2017-02-20 15:13:16 -05:00
9f0484a93b Use new ParallelAPIRequest class 2017-02-20 13:37:08 -05:00
93038e61e5 More code style fixes 2017-02-17 11:37:22 -05:00
30b43fd27c Lots of style fixes, with more to come 2017-02-17 10:55:17 -05:00
6efe1ffbc8 Slightly reorganize model hierarchy 2017-02-17 08:39:27 -05:00
8898655a49 Various tweaking 2017-02-17 08:25:19 -05:00
07b1422fad Reference svg logos as image files, not raw html 2017-02-16 14:30:39 -05:00
0232d18f1f Yet more snake case to camel case 2017-02-16 14:30:06 -05:00
0cef44c986 More javascript style fixes 2017-02-16 13:22:26 -05:00
6ed755e252 JS style fixes 2017-02-16 11:47:54 -05:00
85cd77267b Make sure header comments are actually updated for all code files 2017-02-16 11:09:37 -05:00
488a01f8a5 Js snake case to camel case 2017-02-15 16:58:08 -05:00
ee2760b2b5 Rename test base class 2017-02-15 16:40:18 -05:00
84cb1cb520 And more snake case to camel case 2017-02-15 16:30:14 -05:00
db07976403 Update header comments 2017-02-15 16:13:32 -05:00
c96a3bbd50 Snake case to camel case 2017-02-15 16:11:52 -05:00
8cbfaf3646 More snake case to camel case 2017-02-15 15:56:10 -05:00
29c04e62be More code styles fixes 2017-02-15 15:35:41 -05:00
1224882092 Javascript style fixes 2017-02-15 14:08:15 -05:00
df8b64cff9 Snake case to camel case 2017-02-15 14:07:22 -05:00
ae42fafe84 Update headers 2017-02-15 13:08:17 -05:00
da9ebe8867 More style fixes 2017-02-15 13:07:36 -05:00
0a72e60f68 Fix more code style issues 2017-02-15 11:57:29 -05:00
36874cbe55 Properly namespace all the tests 2017-02-15 11:49:38 -05:00
d94a280437 Snake case to camel case 2017-02-15 11:30:16 -05:00
470c39cf79 Remove some unused code 2017-02-15 11:18:55 -05:00
941a15c4f4 Add yarn lock file 2017-02-15 11:05:03 -05:00
3caad13577 Try, try again 2017-02-15 10:23:07 -05:00
acbae86a6b Slim build config 2017-02-15 10:12:18 -05:00
34d0aaa8e5 Ignore stupid xsl requirement 2017-02-15 10:05:06 -05:00
7941303987 Install xsl because of the stupid dev dependency 2017-02-15 09:57:08 -05:00
da8b34a867 Will xdebug work? 2017-02-15 09:53:46 -05:00
9bf362e08e Will xdebug work? 2017-02-15 09:50:13 -05:00
6ee319b78c xdebug try again 2017-02-15 09:47:52 -05:00
90f1b39db5 Try to use xdebug another way 2017-02-15 09:42:20 -05:00
85a6fafd4f Make sure to try to install the correct packages 2017-02-15 09:36:51 -05:00
2bdf4be682 Remove unneeded bashism 2017-02-15 09:32:31 -05:00
455adf4b11 Maybe this will work better? 2017-02-15 09:30:27 -05:00
9e49566641 Maybe stages will help? 2017-02-15 09:26:49 -05:00
d1f0ab0c73 Attempt to use alpine php image 2017-02-15 09:21:08 -05:00
79153ae433 Attempt testing hhvm with a different docker image 2017-02-15 08:55:51 -05:00
9c44b5189f Merge branch 'develop' into 'master'
Develop

See merge request !13
2017-02-14 16:39:37 -05:00
312125f182 Add hummingbird favicon 2017-02-14 16:23:18 -05:00
652eac5be0 Get sync-lists command to create missing entries on MAL 2017-02-14 15:29:13 -05:00
dba0d47789 Uncomment rewatching stuff 2017-02-13 13:33:01 -05:00
4a3be8b4bf Fix mapping from Kitsu to MAL for updating a list item 2017-02-13 12:42:05 -05:00
7f5966a147 Attempt to fix hhvm tests 2017-02-10 16:33:42 -05:00
8f8f528823 Fix config mapping for BaseCommand 2017-02-10 16:12:02 -05:00
1ec7322b18 Split user config from application config 2017-02-10 15:50:07 -05:00
8f8c413927 Fix update requests broken by Artax conversion 2017-02-09 20:10:13 -05:00
50c543218b Remove 'fix' for issue caused by php.ini setting 2017-02-09 13:45:40 -05:00
18af49f1f4 Replace Guzzle with Artax 2017-02-09 13:44:56 -05:00
906a1f1efa Another ugly progress commit
- Eradicated Guzzle from main codebase
- All API requests now use Artax
- Refactor code to use function and constant imports
- And more!
2017-02-08 15:48:20 -05:00
deecb5a912 Start of work to replace Guzzle with Artax 2017-02-08 00:44:57 -05:00
02838c5024 Update headers and some whitespace 2017-02-07 13:27:41 -05:00
bf7f6973a4 Create Request Builder wrapper around Artax 2017-02-07 13:11:42 -05:00
c0b54e11e1 Update PHPUnit 2017-02-07 09:13:13 -05:00
0fe01e14e1 Only translate fields that are passed in 2017-02-07 09:12:44 -05:00
2f71a97327 Merge branch 'develop' into 'master'
Develop

See merge request !12
2017-02-06 11:51:58 -05:00
e73ea09ffd Update test config to allow hhvm failures 2017-02-06 11:35:21 -05:00
bc0c3774eb Update deprecated test 2017-02-06 11:00:18 -05:00
b10487ac13 Update changelog 2017-02-06 10:57:38 -05:00
64ed6cc0ef Update README 2017-02-06 10:56:27 -05:00
b6c2aee17a Update dependencies 2017-02-06 10:42:41 -05:00
5eea985828 Actually update MAL if enabled 2017-02-04 15:18:34 -05:00
1835e34690 Able to create list items on MAL 2017-02-01 09:53:02 -05:00
2d0fa51c40 Delete outdated test data 2017-01-31 12:55:28 -05:00
108c649a91 Misc updates 2017-01-31 12:52:43 -05:00
c26a4ca8e8 More test coverage for transformers 2017-01-31 12:51:14 -05:00
9d9c1e2cce Add license 2017-01-27 16:34:03 -05:00
2cacff6b9b Add coverage button to readme 2017-01-27 15:56:40 -05:00
47a43517cb More test coverage, attempt to get Gitlab to see test coverage 2017-01-27 15:41:52 -05:00
1b5590bda7 Merge branch 'develop' into 'master'
Develop

See merge request !10
2017-01-27 13:03:53 -05:00
91a6d76c4b Attempt to show code coverage 2017-01-27 12:42:57 -05:00
03964c446a Ugly progress commit 2017-01-27 12:35:28 -05:00
74897faa78 Add back zlib to the docker build 2017-01-27 12:26:32 -05:00
2b2ec71edd Add back dependency for xsl in docker build 2017-01-27 12:17:02 -05:00
a80a860f8d Attempt simpler php setup, with xdebug 2017-01-27 12:09:05 -05:00
3d54a0d62c Another attempt at hhvm testing 2017-01-27 11:38:18 -05:00
aabdf4de30 Attempt to fix hhvm pipeline 2017-01-27 11:21:25 -05:00
20c3d69717 Get xml parsing working predictably 2017-01-27 09:43:42 -05:00
f57c24abe4 Make sure to pass the correct arguments to the cache hash method 2017-01-26 13:06:35 -05:00
0ae52a13f3 Refactor KitsuModel, add more docblocks 2017-01-26 13:03:38 -05:00
ece4f343e2 Fix broken test 2017-01-26 13:02:18 -05:00
b2fb562de6 Fix display of streaming links in cover and list views 2017-01-25 13:37:39 -05:00
19c2d0fddc Merge branch 'develop' into 'master'
Develop

See merge request !9
2017-01-25 12:16:50 -05:00
b2544fab16 Fix issue with selected list highlighting, fixes #20 2017-01-25 12:13:37 -05:00
96cbf78e28 Fix cache clear command 2017-01-19 12:49:18 -05:00
e30a0b867d Merge branch 'develop' 2017-01-17 12:52:02 -05:00
11f9e41254 Update commands 2017-01-17 12:47:02 -05:00
a45eba3c56 Update cache dependency 2017-01-17 12:46:47 -05:00
9bc22baa80 Cache manga list 2017-01-16 14:14:45 -05:00
7bad51d867 Fix failing test 2017-01-16 14:05:42 -05:00
f441b7680a Update views for collection, remove old json import 2017-01-16 14:03:30 -05:00
10368fabe4 Fix anime collection 2017-01-16 13:49:51 -05:00
a42a9bc785 Update README to be more accurate 2017-01-16 12:42:30 -05:00
42f152b366 Merge branch 'develop' 2017-01-16 11:27:49 -05:00
0ec8d6d6b3 Restore cache clearing functionality 2017-01-16 11:26:19 -05:00
1eecff0178 Streaming links, caching, and more MAL integration 2017-01-13 16:53:56 -05:00
3e72a66297 Cache API errors at the dispatcher level, so a more appropriate error page can be displayed 2017-01-13 16:52:12 -05:00
79c0c6cf90 Update search to bring in My anime list id for future integration 2017-01-13 16:51:31 -05:00
c1724397d3 Update views to show streaming links 2017-01-13 16:49:46 -05:00
9e30783ecb Update transformer tests 2017-01-13 16:48:08 -05:00
496eb68078 Really ugly progress commit 2017-01-12 15:41:20 -05:00
2fe45a6b57 Update Changelog and Readme 2017-01-12 11:31:49 -05:00
aca66ae86d Add test data files 2017-01-11 22:27:36 -05:00
f619c78232 Update config files 2017-01-11 22:26:43 -05:00
d91432d960 Third time's a charm for updating the header comment? 2017-01-11 19:37:14 -05:00
62c06b7731 Start of integration with My Anime List 2017-01-11 19:35:51 -05:00
e060e1b107 Update header comments, with proper newlines 2017-01-11 10:34:24 -05:00
08c40de381 Merge branch 'develop' into 'master'
Replace Hummingbird with Kitsu

See merge request !8
2017-01-11 10:32:10 -05:00
c76bb4d32a Update header comments 2017-01-11 10:31:17 -05:00
93e58874de Merge branch 'master' into 'develop'
Hummingbird to Kitsu

See merge request !7
2017-01-11 10:25:43 -05:00
712956d564 Fix unit tests 2017-01-10 21:13:44 -05:00
39118d63e5 All basic API functionality:
* Anime List Item:
	* Creation
	* Updating
	* Retreiving
	* Deletion

* Manga List Item:
	* Creation
	* Updating
	* Retreiving
	* Deletion

* Anime detail page
* Manga detail page
2017-01-10 12:35:46 -05:00
9f048b739e Fix some javascript issues 2017-01-09 21:38:42 -05:00
6e818b7f45 Anime and Manga editing, incrementing, and deletion 2017-01-09 20:36:48 -05:00
e085754955 Update header comments 2017-01-06 23:34:56 -05:00
98bf1e455f Episode incrementing and update work for anime 2017-01-06 21:39:01 -05:00
9d84398ee7 Better handling of alternate titles, Airing Status and genres for anime list views 2017-01-05 22:24:45 -05:00
09338e9132 Authentication, show edit forms for Anime 2017-01-05 13:41:32 -05:00
239b0c055c Update postcss to actually output compatible css 2017-01-04 13:51:04 -05:00
444e18c1d9 Update css to fit blocks within poster images 2017-01-04 13:40:46 -05:00
f1893d9708 Manga lists and detail pages 2017-01-04 13:16:58 -05:00
3030b6b908 Start of changes for Manga list 2017-01-03 21:06:49 -05:00
cdf9ad0105 Remove some old code to better make way for kitsu/MAL api integration 2017-01-03 20:29:43 -05:00
05c6f22f67 Another ugly progress commit
Sort of working:
* Get anime list by status
* Get anime description pages
2016-12-22 21:36:23 -05:00
e74fe9d2f5 Pull stuff from the Kitsu API 2016-12-21 12:46:20 -05:00
c9f5964d5e Ugly progress commit 2016-12-20 12:58:37 -05:00
e097b02457 Start of API integration 2016-12-20 12:55:43 -05:00
f2fcc8ee93 Remove Hummingbird stuff 2016-12-16 21:52:59 -05:00
513ba4c70f Fix hhvm tests take 2 2016-11-03 11:39:24 -04:00
66e51af8d7 Fix hhvm tests 2016-11-03 11:30:22 -04:00
ba9c41f495 Update CI tools to exclude old PHP versions 2016-11-01 09:10:11 -04:00
07ebb3e988 Update headers and namespaces 2016-10-20 22:32:17 -04:00
c915ea871d Update EVERYTHING 2016-10-20 22:09:36 -04:00
656688a5f3 Make sure to use the version of phpunit I actually install 2016-08-30 12:02:21 -04:00
60c9c86580 Attempt to fix travis build 2016-08-30 11:51:35 -04:00
b0c49ca19c Update Robofile to work properly 2016-08-30 11:45:17 -04:00
dd2d25d54c Code style fixes to satisfy phpcs 2016-08-30 10:57:41 -04:00
230c80459e Update header comments 2016-08-30 10:01:18 -04:00
f7915ba6f2 Move tests to tests/ directory 2016-08-29 17:09:56 -04:00
ac971c5248 Move src files to root of src/ 2016-08-29 16:36:13 -04:00
eaa0e517c1 Build/doc generation updates 2016-08-29 15:50:59 -04:00
4e4ac58263 Fix tests broken due to changes in container 2016-08-29 15:36:36 -04:00
88e06a0052 Convert Dependency injection bootstrap file to use factory functions, rather than direct instances 2016-08-29 14:51:32 -04:00
e0e63cf094 Move Ion namespace into composer dependency 2016-08-09 11:08:45 -04:00
410d45029f Another attempt at hhvm setup 2016-08-03 18:43:09 -04:00
8f3ce089a4 actually use the correct composer command to install phpunit for hhvm 2016-08-03 18:25:28 -04:00
7e05a4dcc2 try a different docker container for running hhvm tests 2016-08-03 18:09:55 -04:00
7b9b62b200 Attempt hhvm testing on gitlab ci, adjust acceptable failures on travis 2016-08-03 14:30:36 -04:00
6bdd33da2a Don't install dev dependencies in test environments 2016-08-03 14:14:38 -04:00
3894ba8312 Another attempt to fix gitlab ci build 2016-08-03 14:08:02 -04:00
473a046ed8 Attempt to fix gitlab ci build 2016-08-03 13:47:14 -04:00
47e67d5d1a Set up mutation testing for unit tests 2016-08-01 14:38:23 -04:00
e62558d607 Add dev dependencies, augment gitignore 2016-08-01 13:10:00 -04:00
6b9770698b Refactor cache to remove dependency on container 2016-08-01 13:02:26 -04:00
893584696b Move cache class to IOn namespace, use safer json for serialization in cache drivers 2016-07-28 10:44:13 -04:00
a108adfa23 Finish moving get_cached_image method to Util class 2016-07-27 14:32:37 -04:00
f00acbe2d6 Fix travis ci tests 2016-07-27 13:35:30 -04:00
563adace2f Refactor out some Interdependency between Ion and AnimeClient namespaces 2016-07-27 13:18:52 -04:00
cc7046f0ec Update the correct file to change config for gitlab ci 2016-07-25 12:29:42 -04:00
b415938583 Attempt moving config file in a different way 2016-07-25 12:18:51 -04:00
2a41106638 Fix typo in test path 2016-07-25 12:04:56 -04:00
cb046dbde2 Add redis config file for gitlab ci tests 2016-07-25 11:58:43 -04:00
96d3dfdbb4 Update Redis tests to work with gitlab ci 2016-07-22 17:48:13 -04:00
fa22f7b493 Update Redis cache driver to use PHP-only library, removing the dependence on an extension: 2016-07-22 17:22:00 -04:00
e2c6c14af8 Remove redundant mbstring extesion from build setup 2016-07-19 10:45:16 -04:00
ca62a411e1 Attempt 2 to install gd in gitlab ci tests 2016-07-19 10:25:54 -04:00
10bb167ff4 Fix failing test by installing gd in gitlab ci test 2016-07-19 10:17:53 -04:00
ead6f8b487 Set default timezone to prevent stupid test errors 2016-07-18 13:06:37 -04:00
672552a1e8 Make tests skip redis integration if the extension is not installed 2016-07-18 12:59:34 -04:00
f940606f0c Another attempt at getting gitlab ci to run 2016-07-18 12:47:51 -04:00
b271fd48a6 Attempt tests without redis for now 2016-07-18 10:16:21 -04:00
8e00f3a59d Gitlab CI take 3 2016-07-18 10:07:50 -04:00
ebc8006e04 Make sure docker sh script doesn't have CRLF line endings 2016-07-18 09:58:23 -04:00
c84e76a869 Gitlab CI take two 2016-07-18 09:55:06 -04:00
a178ac86c6 First attempt at setting up gitlab ci 2016-07-18 09:47:34 -04:00
1e18344990 Merge branch 'develop' 2016-07-15 11:35:31 -04:00
6b31e759c9 Minor example file tweaks, add smooth scrolling to browsers that support it 2016-06-07 11:36:02 -04:00
1842e1a30b Remove redundant cache loading 2016-04-22 10:52:42 -04:00
dd20c774ed Shrink api clearing button 2016-04-22 10:47:18 -04:00
b2990e8457 Add button to clear api cache 2016-04-21 11:14:21 -04:00
4e48c8c4fa Update changelog and add additional tests 2016-04-19 14:51:58 -04:00
3605ec6d0b Update README for version 3 2016-04-19 13:24:21 -04:00
2151d64f25 Small miscellaneous cleanup 2016-04-19 13:23:49 -04:00
89080171c9 Add batch image thumbnail creation, see #6, #14 2016-04-19 13:02:50 -04:00
733d3f3871 Resolves #10, adds ability to delete from anime collection 2016-04-14 19:10:03 -04:00
a14d268652 Update sonarqube version 2016-04-14 18:03:34 -04:00
b5949466e4 Add detail view to anime list 2016-04-14 17:51:00 -04:00
1f9afd07f3 Add ability to delete items from manga list. See #10 2016-04-14 17:00:34 -04:00
717c296e52 Add ability to delete items from anime list. References #10 2016-04-14 15:16:13 -04:00
b528b8dc17 Re-add cache to manga controller so cache can be invalidated on update 2016-04-14 11:05:16 -04:00
98f709c36e Add tests for Cache Manager class 2016-04-12 14:05:13 -04:00
330ac65e18 Add missing update to base API Model 2016-04-12 13:41:50 -04:00
e26e9f10b6 Update manga model to cache the one api response. 2016-04-12 13:41:03 -04:00
7d6c6fe2a0 Add NullDriver for cache layer, for the sake of testing, or config without a cache 2016-04-12 12:05:42 -04:00
5d0b879623 Miscellaneous cleanup 2016-04-08 18:05:52 -04:00
1a182a62a7 Remove allowed failures for PHP 5.5 & 5.6 2016-04-08 15:21:10 -04:00
429c9861e3 One last attempt to get redis to work with travis CI 2016-04-08 15:17:45 -04:00
99a5907e1b Another attempt to get redis working 2016-04-08 15:06:16 -04:00
1e42d431c8 Update travis config to test redis 2016-04-08 14:56:09 -04:00
05a3d0e729 Fix anime collection selection template to match db schema 2016-04-08 14:45:11 -04:00
4b4a259f8a Add Redis Cache driver 2016-04-08 14:25:45 -04:00
454324aba6 Update first migration to allow empty notes on collection 2016-04-08 13:39:37 -04:00
470795f21e Add changelog 2016-04-08 13:22:29 -04:00
95abe28322 Remove zepto 2016-04-08 13:22:10 -04:00
e35373a114 Set up package.json for myth css processing 2016-04-08 11:57:16 -04:00
ea1d342db5 Remove target=_blank from links 2016-04-08 11:56:17 -04:00
83a200871d Add caching to Manga views 2016-04-07 13:11:45 -04:00
0f6c998109 Add tests for SQL based api cache 2016-04-07 12:34:57 -04:00
440a999c2e Remove json 'cache' files from anime model 2016-04-07 12:32:32 -04:00
7711765563 Fix tests 2016-04-06 14:58:19 -04:00
7efa180bbf Add some naive cache invalidation to update methods 2016-04-06 12:11:07 -04:00
84e4e6ce1b Start of caching implementation 2016-04-05 13:19:35 -04:00
972d96c0e0 Fix search methods to work with new Request library 2016-04-05 12:06:07 -04:00
3bcb0442ca Add migration for sql cache backend 2016-04-05 12:03:56 -04:00
2afbe84afd Start of interface for caching backend 2016-04-01 17:35:53 -04:00
06f07978dc Fix broken tests 2016-03-29 11:40:27 -04:00
2dde6f8a7d Fix issue with cover not being hidden on last episode 2016-03-29 11:30:51 -04:00
c8789dc267 Update url generation to use new router 2016-03-07 14:37:49 -05:00
ed16fd8d45 Fix most of the broken tests 2016-03-03 16:53:17 -05:00
dd74e85626 Further refactor handling of request variables, routing works again 2016-02-17 11:36:37 -05:00
ab19e9db08 Get HTML output working again, still refactoring router 2016-02-17 10:29:05 -05:00
98f3026a74 Start integration of PSR 7 Request/Response 2016-02-16 16:28:44 -05:00
1ba302999d Merge remote-tracking branch 'origin/master' into develop 2016-02-16 12:26:54 -05:00
197a1f8326 Update some javascript documentation, and add show/hide methods 2016-02-16 12:07:01 -05:00
9cc491a05e Minor style fixes, and fix double message issue 2016-02-10 17:56:46 -05:00
bcd28acfc5 Minor style fixes, and fix double message issue 2016-02-10 17:50:07 -05:00
d99f1e7595 Start of migration from php to toml config, see #11 2016-02-10 17:30:45 -05:00
1fb95adfbf Merge branch 'develop' into 'master'
Version 2.2

Pull Request tracking changes leading up to version 2.2

See merge request !1
2016-02-10 12:29:39 -05:00
e8cc479a1e Better front-end tests 2016-02-10 12:25:13 -05:00
fb29c90691 Update README 2016-02-09 21:42:56 -05:00
3027c8a4a8 Update tests relating to issue #9 2016-02-09 21:03:26 -05:00
0b6269edd6 Polyfill classList api for browsers lacking support 2016-02-09 20:57:40 -05:00
12d05dd71a Fixes issue #9 2016-02-09 20:20:54 -05:00
ecf3bce14b Partially fix #9 -- API calls fail on 'Plan to Watch' section 2016-02-09 20:12:39 -05:00
5563902b69 Combine javascript library files into one base file 2016-02-09 20:07:01 -05:00
874ad521a7 Update README with some server setup details, resolves #7 2016-02-09 16:45:22 -05:00
a9f5e48bb2 Remove last dependencies on zepto 2016-02-08 20:21:41 -05:00
cbc7555cf2 Remove zepto ajax calls 2016-02-08 13:37:44 -05:00
e08161aec2 Further improve minifiers, add start of front-end tests 2016-02-08 11:32:39 -05:00
7754377ecb Fix #8, make minfiers output correctly 2016-02-08 10:57:44 -05:00
0b2a0d4ea9 Add basic htaccess file for apache 2016-02-05 14:14:02 -05:00
94f54366a7 Some temporary fixes for tempramental minifiers 2016-02-04 21:57:14 -05:00
f5dc15659f Make build.xml file more phing friendly 2016-02-03 21:24:10 -05:00
1a21a23e73 Rewrite minifiers into cleaner classes. Resolves #5 2016-02-03 14:57:00 -05:00
4533ea0b26 Minor spacing fixes 2016-02-02 21:38:38 -05:00
540a82fe22 Minor code quality fixes for Scrutinizer 2016-02-02 21:28:32 -05:00
467763f8a6 Fix manga editing for sections other than 'Reading' 2016-02-02 14:13:49 -05:00
99f2adf491 Add form for manga 2016-02-02 11:34:03 -05:00
3fa8e7d8e8 Ugly progress commit 2016-02-01 09:49:18 -05:00
9080f45601 Add missing table sorting lib 2016-01-20 20:14:32 -05:00
08348fd349 Add proper table sorting and add some security headers 2016-01-20 13:01:41 -05:00
e109c6a06c Another attempt at code coverage for codeclimate 2016-01-12 12:47:48 -05:00
457c2680b6 Code coverage for codeclimate 2016-01-12 12:33:45 -05:00
a85f0e28c6 Merge pull request #13 from timw4mail/develop
Sync with dev
2016-01-11 15:44:57 -05:00
b611d02a2c Change logger methods to be inline with interface, fix Manga Model tests 2016-01-11 15:31:53 -05:00
e51159e7f3 Update whoops to 2.0 2016-01-11 14:57:43 -05:00
2391ca14ca Remove errorhandler, and replace with logger 2016-01-11 14:39:53 -05:00
212c779552 Update collection to use flash messages and more intelligent redirects 2016-01-11 13:33:56 -05:00
0c2243a4f3 Add more test coverage, and update build.xml 2016-01-11 10:42:34 -05:00
3981b8471a Further refine Dispatcher 2016-01-08 16:39:18 -05:00
276a14a80c Refactor Dispatcher 2016-01-08 15:54:21 -05:00
b4949eaea2 Fix line endings in view classes 2016-01-08 15:53:50 -05:00
993b625042 Actually fix view tests 2016-01-08 11:40:24 -05:00
557b27ef77 Fix view tests 2016-01-08 11:19:56 -05:00
45b04105f1 Fix http verb for update route, add correct http codes for http errors 2016-01-07 20:48:18 -05:00
ae4530b5d2 Update codebase to use new Json class 2016-01-07 13:45:43 -05:00
00fd23895d Add missing classes 2016-01-06 17:08:44 -05:00
aa67b941d4 Fix PHP 5.5 build 2016-01-06 17:06:30 -05:00
b94bf01dee Remove unnamespaced constants, and improve some tests 2016-01-06 15:44:40 -05:00
ba94f439bb Simplify routing 2016-01-06 11:08:56 -05:00
e3af767246 Fix testss 2016-01-05 10:05:14 -05:00
b0f5bdf668 Merge pull request #12 from timw4mail/scrutinizer-patch-2
Scrutinizer Auto-Fixes
2016-01-05 10:02:18 -05:00
Scrutinizer Auto-Fixer
2b5d650ef8 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2016-01-05 14:55:08 +00:00
e9e16dd2b1 Update header comments, add start of manga editing functionality 2016-01-04 16:58:33 -05:00
6b9be7e4d0 Merge pull request #11 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2016-01-04 11:16:15 -05:00
Scrutinizer Auto-Fixer
8b528d8659 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2016-01-04 16:15:25 +00:00
9c81836648 Add full edit form to anime list 2016-01-04 10:53:03 -05:00
c04c85b999 Update composer.json 2015-12-16 10:12:31 -05:00
6953cd08e6 Start of delete functionality for anime collection 2015-12-15 15:55:30 -05:00
c82576e4dd Merge pull request #10 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-12-09 15:13:24 -05:00
Scrutinizer Auto-Fixer
b70ff95f03 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-12-09 19:59:54 +00:00
30b6afb601 Some more minor code-style fixes 2015-12-09 14:54:11 -05:00
6ca086d85b Some code style fixes 2015-12-08 16:39:49 -05:00
bfb8500386 update travis build file 2015-12-08 14:58:43 -05:00
70bfe2bab7 Fix collection functionality 2015-12-08 14:52:59 -05:00
7d1c8c383c Update README and composer 2015-11-18 16:03:40 -05:00
3b876f2c42 Skip erroring tests on travis 2015-11-18 10:58:12 -05:00
56f9fa28aa Fix some minor formating issues 2015-11-18 10:54:06 -05:00
2577bad0af Update minor documention issues 2015-11-18 10:48:05 -05:00
ed1e888c58 Try mocking out get_cached_image method 2015-11-18 10:41:00 -05:00
254afc990e More test coverage 2015-11-18 10:31:42 -05:00
8d1986d13b Improve some test coverage 2015-11-17 16:45:41 -05:00
5d2cad4690 Remove loose functions file 2015-11-16 19:30:04 -05:00
49b3f507a6 Fix manga list updating 2015-11-16 15:57:37 -05:00
912f1b2ff2 Merge pull request #9 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-11-16 11:40:26 -05:00
f75016ca69 Update header comments 2015-11-16 11:40:01 -05:00
Scrutinizer Auto-Fixer
4e1bcd962a Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-11-16 15:33:30 +00:00
69f35d222c Poor style progress update commit 2015-11-13 16:31:01 -05:00
898905c21c Update some config and metadata 2015-11-13 11:34:30 -05:00
f26adc1c28 Update 404 view 2015-11-13 11:33:47 -05:00
dfe91d0f1c Make updating of anime list work 2015-11-13 11:33:27 -05:00
05912cd540 Update js minifier to be more robust, with better error handling 2015-11-13 11:32:12 -05:00
ab955f5154 Fix various code style nuances 2015-11-11 15:28:51 -05:00
8343fa9182 Fix some sonarqube issues 2015-11-11 14:53:09 -05:00
406c7c13cb Merge pull request #8 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-11-09 15:55:54 -05:00
Scrutinizer Auto-Fixer
cd8185960b Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-11-09 20:08:08 +00:00
87f5324761 Update config and header for new auth class 2015-11-09 11:50:24 -05:00
a18f1926d9 Fix ArrayType class 2015-11-09 11:49:51 -05:00
17a335275c More quality fixes 2015-11-09 11:10:15 -05:00
ff13f2ce05 No coverage for scrutinizer 2015-11-05 11:30:51 -05:00
4937d4c099 Fix some more code style issues 2015-11-05 11:26:03 -05:00
1de8b46657 Some more style fixes 2015-11-05 10:41:46 -05:00
6aa51e5915 Some code style fixes 2015-11-04 16:53:22 -05:00
8936944743 Some minor refactoring 2015-11-04 16:36:54 -05:00
76672d5a60 Merge pull request #7 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-11-04 16:33:18 -05:00
Scrutinizer Auto-Fixer
025bc4ef64 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-11-04 21:31:03 +00:00
a741526da1 Update metadata and build information files 2015-11-04 16:12:46 -05:00
799a3652d4 Fix tests broken by missing fix to Anime Collection Model 2015-10-21 15:46:50 -04:00
2f8886c28f More test coverage 2015-10-21 15:43:51 -04:00
c3643565e3 Fix issue where cache file doesn't exist, add tests for Menu Helper 2015-10-21 11:57:58 -04:00
28e03f6fb2 Fix default redirect and tests 2015-10-20 16:41:51 -04:00
4ef2d6df57 Fix the rest of the menu urls 2015-10-20 15:59:51 -04:00
779f4a00eb Remove another vistigal controller method 2015-10-19 15:19:02 -04:00
d281c26a1c Remove risky tests, update .gitignore 2015-10-19 15:13:18 -04:00
77399e3fa4 Remove some vestigal methods from base controller 2015-10-19 13:58:59 -04:00
5f5bf66c75 Fix spacing style 2015-10-19 13:26:50 -04:00
385037b669 Fix tests for PHP 5.5 2015-10-19 13:02:10 -04:00
fee09c50ae More test coverage 2015-10-19 12:50:46 -04:00
285a132d35 Scrutinizer fixes 2015-10-16 12:53:55 -04:00
0b79ac3596 Better testing for ArrayType and Config classes 2015-10-15 22:00:09 -04:00
de444857dd Add partial test for config delete 2015-10-15 10:23:00 -04:00
d231b39fdc Fix origin value in API Model tests 2015-10-15 09:49:38 -04:00
d5c76a0f01 Fix html view test for PHP < 7 2015-10-15 09:28:10 -04:00
485ba46838 More test coverage 2015-10-15 09:25:30 -04:00
7f2000f180 Update lots of docblocks 2015-10-14 09:20:52 -04:00
34acf00b5e Fix documentation issues 2015-10-12 14:27:20 -04:00
bc6aed51c8 Remove some dead code 2015-10-12 14:11:00 -04:00
011bdda777 Merge pull request #6 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-10-12 11:00:09 -04:00
Scrutinizer Auto-Fixer
80f83e5f53 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-10-10 02:35:39 +00:00
b687dcf2a8 Rearrange some namespaces and add more docblocks 2015-10-09 22:29:59 -04:00
bb3e5a643f Merge pull request #5 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-10-09 15:04:55 -04:00
Scrutinizer Auto-Fixer
4dddd3238c Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-10-09 18:55:15 +00:00
a8e3c594e3 Basic Menu generation 2015-10-09 14:34:55 -04:00
728850da08 More scrutinizer fixes 2015-10-06 13:38:59 -04:00
443802332b Merge pull request #4 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-10-06 13:37:48 -04:00
Scrutinizer Auto-Fixer
bf6550d0d9 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-10-06 17:35:42 +00:00
37ef0f5621 Fix more scrutinizer issues 2015-10-06 12:15:19 -04:00
38cfaa023d Fix failing test 2015-10-06 11:41:21 -04:00
f3772528c5 Code style improvements 2015-10-06 11:38:20 -04:00
dbbea163d1 Scrutinizer fixes 2015-10-06 10:44:33 -04:00
7269c5c393 Merge pull request #3 from timw4mail/scrutinizer-patch-1
Spacing and docblock fixes
2015-10-06 10:28:33 -04:00
Scrutinizer Auto-Fixer
7e695edd29 Scrutinizer Auto-Fixes
This commit consists of patches automatically generated for this project on https://scrutinizer-ci.com
2015-10-06 14:24:48 +00:00
6b322d18da Miscellaneous updates, prep for menu generator 2015-10-05 16:54:25 -04:00
be96e2c6af Update Router 2015-10-01 16:30:46 -04:00
5de88986ed Update some meta files 2015-10-01 16:21:09 -04:00
b68cbe5a26 fix test 2015-10-01 16:07:40 -04:00
2462e09205 Lots of miscellaneous improvements 2015-10-01 16:02:51 -04:00
acb0a1e8a2 Fix views to match transformed data 2015-10-01 16:01:23 -04:00
a73e150ee3 Fix broken test 2015-09-28 15:11:45 -04:00
b8f753a424 Use Anime transformer class 2015-09-28 14:41:45 -04:00
23c16fc9c3 Transformers and Enums 2015-09-25 13:41:12 -04:00
082b5296b9 Update manga model to use Zipper transformer 2015-09-21 09:48:15 -04:00
f87dd2636d More tests for Ion 2015-09-18 22:55:40 -04:00
60109b3531 Fix failing tests for PHP < 5.6 2015-09-18 13:06:22 -04:00
e2731db7ea Decouple and generalise 2015-09-17 23:11:18 -04:00
156461a0b9 Start of refactoring routing to be more convention based 2015-09-16 12:25:35 -04:00
8a68559f0e More namespace refactoring 2015-09-15 13:19:29 -04:00
97a5abe665 Namespace refactoring 2015-09-14 19:54:34 -04:00
d93f5a82a0 fix a few variable changes, remove old code from app folder 2015-09-14 16:14:02 -04:00
9d139a7d1c Pass the tests! 2015-09-14 15:49:20 -04:00
e53f9abf3f Some progress toward better structure through refactoring 2015-09-14 10:54:50 -04:00
c5f3093a78 Update default config, add phpci config file 2015-07-20 16:13:00 -04:00
4622b6efa9 Update readme with new instructions for collection 2015-07-06 14:35:24 -04:00
3e39aa0277 Miscellaneous rework, and adding/editing of collection items when logged in 2015-07-02 14:04:04 -04:00
3e53ec1526 More dependency injection, and code coverage 2015-06-30 13:03:20 -04:00
2abb5f3c3a Merge pull request #1 from timw4mail/scrutinizer-patch-1
Scrutinizer Auto-Fixes
2015-06-29 10:36:28 -04:00
526 changed files with 50872 additions and 22713 deletions

10
.gitignore vendored
View File

@ -146,4 +146,12 @@ public/images/manga/**
public/images/characters/**
public/images/people/**
public/mal_mappings.json
.phpunit.result.cache
.phpunit.result.cache
.is-dev
tmp
tools/vendor/
tools/phinx/vendor/
/.php-cs-fixer.php
/.php-cs-fixer.cache

534
.php-cs-fixer.dist.php Normal file
View File

@ -0,0 +1,534 @@
<?php declare(strict_types=1);
use Nexus\CsConfig\Factory;
use PhpCsFixer\{Config, Finder};
$finder = Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__ . '/tools',
])
->exclude([
'vendor',
]);
return (new Config())
->setRiskyAllowed(TRUE)
->setFinder($finder)
->setIndent(' ')
->setRules([
'align_multiline_comment' => false,
'array_indentation' => true,
'array_push' => true,
'array_syntax' => ['syntax' => 'short'],
'assign_null_coalescing_to_coalesce_equal' => true,
'backtick_to_shell_exec' => true,
'binary_operator_spaces' => [
'default' => 'single_space',
'operators' => [
'=' => NULL,
'&' => NULL,
]
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => false,
'blank_line_before_statement' => [
'statements' => [
// 'case',
'continue',
'declare',
'default',
'do',
'exit',
'for',
'foreach',
'goto',
'return',
'switch',
'throw',
'try',
'while',
'yield',
'yield_from',
],
],
// 'braces' => [
// 'allow_single_line_anonymous_class_with_empty_body' => true,
// 'allow_single_line_closure' => true,
// 'position_after_anonymous_constructs' => 'same',
// 'position_after_control_structures' => 'next',
// 'position_after_functions_and_oop_constructs' => 'next',
// ],
'cast_spaces' => ['space' => 'single'],
'class_attributes_separation' => [
'elements' => [
'const' => 'none',
'property' => 'none',
'method' => 'one',
'trait_import' => 'none',
],
],
'class_definition' => [
'multi_line_extends_each_single_line' => true,
'single_item_single_line' => true,
'single_line' => true,
'space_before_parenthesis' => true,
],
'class_reference_name_casing' => true,
'clean_namespace' => true,
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'combine_nested_dirname' => true,
'comment_to_phpdoc' => [
'ignored_tags' => [
'todo',
'codeCoverageIgnore',
'codeCoverageIgnoreStart',
'codeCoverageIgnoreEnd',
'phpstan-ignore-line',
'phpstan-ignore-next-line',
],
],
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'constant_case' => ['case' => 'upper'],
'control_structure_braces' => true,
'control_structure_continuation_position' => ['position' => 'next_line'],
'curly_braces_position' => [
'allow_single_line_anonymous_functions' => true,
'allow_single_line_empty_anonymous_classes' => true,
'anonymous_functions_opening_brace' => 'same_line',
'classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
'control_structures_opening_brace' => 'next_line_unless_newline_at_signature_end',
'functions_opening_brace' => 'next_line_unless_newline_at_signature_end',
],
'date_time_immutable' => false,
'declare_equal_normalize' => ['space' => 'none'],
'declare_parentheses' => true,
'declare_strict_types' => true,
'dir_constant' => true,
'doctrine_annotation_array_assignment' => false,
'doctrine_annotation_braces' => false,
'doctrine_annotation_indentation' => false,
'doctrine_annotation_spaces' => false,
'echo_tag_syntax' => [
'format' => 'short',
'long_function' => 'echo',
'shorten_simple_statements_only' => false,
],
'elseif' => false,
'empty_loop_body' => ['style' => 'braces'],
'empty_loop_condition' => ['style' => 'while'],
'encoding' => true,
'error_suppression' => [
'mute_deprecation_error' => true,
'noise_remaining_usages' => false,
'noise_remaining_usages_exclude' => [],
],
'escape_implicit_backslashes' => [
'double_quoted' => false,
'heredoc_syntax' => false,
'single_quoted' => false,
],
'explicit_indirect_variable' => false,
'explicit_string_variable' => false,
'final_class' => false,
'final_internal_class' => [
'annotation_exclude' => ['@no-final'],
'annotation_include' => ['@internal'],
'consider_absent_docblock_as_internal_class' => false,
],
'final_public_method_for_abstract_class' => false,
'fopen_flag_order' => true,
'fopen_flags' => ['b_mode' => true],
'full_opening_tag' => true,
'fully_qualified_strict_types' => true,
'function_declaration' => ['closure_function_spacing' => 'one'],
'function_to_constant' => [
'functions' => [
'get_called_class',
'get_class',
'get_class_this',
'php_sapi_name',
'phpversion',
'pi',
],
],
'function_typehint_space' => true,
'general_phpdoc_annotation_remove' => false,
'general_phpdoc_tag_rename' => false,
'get_class_to_class_keyword' => false,
'global_namespace_import' => [
'import_constants' => true,
'import_functions' => true,
'import_classes' => true,
],
'group_import' => true,
'header_comment' => false, // false by default
// 'heredoc_indentation' => ['indentation' => 'start_plus_one'],
'heredoc_to_nowdoc' => true,
'implode_call' => true,
'include' => true,
'increment_style' => ['style' => 'post'],
'indentation_type' => true,
'integer_literal_case' => true,
'is_null' => true,
'lambda_not_used_import' => true,
'line_ending' => true,
'linebreak_after_opening_tag' => false,
'list_syntax' => ['syntax' => 'short'],
'logical_operators' => true,
'lowercase_cast' => true,
'lowercase_keywords' => true,
'lowercase_static_reference' => true,
'magic_constant_casing' => true,
'magic_method_casing' => true,
'mb_str_functions' => false,
'method_argument_space' => [
'after_heredoc' => false,
'keep_multiple_spaces_after_comma' => false,
'on_multiline' => 'ensure_fully_multiline',
],
'method_chaining_indentation' => true,
'modernize_strpos' => false, // requires 8.0+
'modernize_types_casting' => true,
'multiline_comment_opening_closing' => true,
'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'],
'native_constant_invocation' => false,
'native_function_casing' => true,
'native_function_invocation' => false,
'native_function_type_declaration_casing' => true,
'new_with_braces' => true,
'no_alias_functions' => ['sets' => ['@all']],
'no_alias_language_construct_call' => true,
'no_alternative_syntax' => ['fix_non_monolithic_code' => false],
'no_binary_string' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_blank_lines_before_namespace' => false, // conflicts with `single_blank_line_before_namespace`
'no_break_comment' => ['comment_text' => 'no break'],
'no_closing_tag' => true,
'no_empty_comment' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_extra_blank_lines' => ['tokens' => ['extra']],
'no_homoglyph_names' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_mixed_echo_print' => ['use' => 'echo'],
'no_multiline_whitespace_around_double_arrow' => true,
'no_null_property_initialization' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_space_around_double_colon' => true,
'no_spaces_after_function_name' => true,
'no_spaces_around_offset' => ['positions' => ['inside', 'outside']],
'no_spaces_inside_parenthesis' => true,
'no_superfluous_elseif' => true,
'no_superfluous_phpdoc_tags' => [
'allow_mixed' => true,
'allow_unused_params' => true,
'remove_inheritdoc' => false,
],
'no_trailing_comma_in_singleline' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'no_trailing_whitespace_in_string' => true,
'no_unneeded_control_parentheses' => [
'statements' => [
'break',
'clone',
'continue',
'echo_print',
'return',
'switch_case',
'yield',
],
],
'no_unneeded_curly_braces' => ['namespaces' => true],
'no_unneeded_final_method' => ['private_methods' => true],
'no_unneeded_import_alias' => true,
'no_unreachable_default_argument_value' => true,
'no_unset_cast' => true,
'no_unset_on_property' => false,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_useless_sprintf' => true,
'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
'no_whitespace_in_blank_line' => true,
'non_printable_character' => ['use_escape_sequences_in_strings' => true],
'normalize_index_brace' => true,
'not_operator_with_space' => true,
'not_operator_with_successor_space' => true,
'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => true],
'object_operator_without_whitespace' => true,
'operator_linebreak' => ['only_booleans' => true, 'position' => 'beginning'],
'ordered_class_elements' => [
'order' => [
'use_trait',
'case',
'constant_public',
'constant_protected',
'constant_private',
'property_public',
'property_protected',
'property_private',
'construct',
'destruct',
'magic',
],
'sort_algorithm' => 'none',
],
'ordered_imports' => [
'sort_algorithm' => 'alpha',
'imports_order' => ['class', 'function', 'const'],
],
'ordered_interfaces' => false,
'ordered_traits' => false,
'php_unit_construct' => [
'assertions' => [
'assertSame',
'assertEquals',
'assertNotEquals',
'assertNotSame',
],
],
'php_unit_dedicate_assert' => ['target' => 'newest'],
'php_unit_dedicate_assert_internal_type' => ['target' => 'newest'],
'php_unit_expectation' => ['target' => 'newest'],
'php_unit_fqcn_annotation' => true,
'php_unit_internal_class' => ['types' => ['final']],
'php_unit_method_casing' => ['case' => 'camel_case'],
'php_unit_mock' => ['target' => 'newest'],
'php_unit_mock_short_will_return' => true,
'php_unit_namespaced' => ['target' => 'newest'],
'php_unit_no_expectation_annotation' => [
'target' => 'newest',
'use_class_const' => true,
],
'php_unit_set_up_tear_down_visibility' => true,
'php_unit_size_class' => false,
// 'php_unit_strict' => [
// 'assertions' => [
// 'assertAttributeEquals',
// 'assertAttributeNotEquals',
// 'assertEquals',
// 'assertNotEquals',
// ],
// ],
'php_unit_test_annotation' => ['style' => 'prefix'],
'php_unit_test_case_static_method_calls' => [
'call_type' => 'this',
'methods' => [],
],
'php_unit_test_class_requires_covers' => false,
'phpdoc_add_missing_param_annotation' => ['only_untyped' => true],
'phpdoc_align' => [
'align' => 'left'
],
'phpdoc_annotation_without_dot' => false,
'phpdoc_indent' => true,
'phpdoc_inline_tag_normalizer' => [
'tags' => [
'example',
'id',
'internal',
'inheritdoc',
'inheritdocs',
'link',
'source',
'toc',
'tutorial',
],
],
'phpdoc_line_span' => [
'const' => 'multi',
'method' => 'multi',
'property' => 'multi',
],
'phpdoc_no_access' => true,
'phpdoc_no_empty_return' => false,
'phpdoc_no_package' => false,
'phpdoc_no_useless_inheritdoc' => true,
'phpdoc_order' => true,
'phpdoc_order_by_value' => [
'annotations' => [
'author',
'covers',
'coversNothing',
'dataProvider',
'depends',
'group',
'internal',
'method',
'property',
'property-read',
'property-write',
'requires',
'throws',
'uses',
],
],
'phpdoc_return_self_reference' => [
'replacements' => [
'this' => '$this',
'@this' => '$this',
'$self' => 'self',
'@self' => 'self',
'$static' => 'static',
'@static' => 'static',
],
],
'phpdoc_scalar' => [
'types' => [
'boolean',
'callback',
'double',
'integer',
'real',
'str',
],
],
'phpdoc_separation' => false,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => false,
'phpdoc_tag_casing' => ['tags' => ['inheritDoc']],
'phpdoc_tag_type' => ['tags' => ['inheritDoc' => 'inline']],
'phpdoc_to_comment' => false,
'phpdoc_to_param_type' => false,
'phpdoc_to_property_type' => false,
'phpdoc_to_return_type' => false,
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'phpdoc_types' => ['groups' => ['simple', 'alias', 'meta']],
'phpdoc_types_order' => [
'null_adjustment' => 'always_last',
'sort_algorithm' => 'alpha',
],
'phpdoc_var_annotation_correct_order' => true,
'phpdoc_var_without_name' => true,
'pow_to_exponentiation' => true,
'protected_to_private' => true,
'psr_autoloading' => ['dir' => null],
'random_api_migration' => [
'replacements' => [
'getrandmax' => 'mt_getrandmax',
'rand' => 'mt_rand',
'srand' => 'mt_srand',
],
],
'regular_callable_call' => true,
'return_assignment' => true,
'return_type_declaration' => ['space_before' => 'none'],
'self_accessor' => false,
'self_static_accessor' => true,
'semicolon_after_instruction' => false,
'set_type_to_cast' => true,
'short_scalar_cast' => true,
'simple_to_complex_string_variable' => true,
'simplified_if_return' => true,
'simplified_null_return' => false,
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
'single_class_element_per_statement' => ['elements' => ['const', 'property']],
'single_import_per_statement' => false,
'single_line_after_imports' => true,
'single_line_comment_style' => ['comment_types' => ['asterisk', 'hash']],
'single_line_throw' => false,
'single_quote' => ['strings_containing_single_quote_chars' => false],
'single_space_around_construct' => [
'constructs_followed_by_a_single_space' => [
'abstract',
'as',
'attribute',
'break',
'case',
'catch',
'class',
'clone',
'comment',
'const',
'const_import',
'continue',
'do',
'echo',
'else',
'elseif',
'extends',
'final',
'finally',
'for',
'foreach',
'function',
'function_import',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'match',
'named_argument',
'new',
'open_tag_with_echo',
'php_doc',
'php_open',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'return',
'static',
'throw',
'trait',
'try',
'use',
'use_lambda',
'use_trait',
'var',
'while',
'yield',
'yield_from',
],
],
'single_trait_insert_per_statement' => true,
'space_after_semicolon' => ['remove_in_empty_for_expressions' => true],
'standardize_increment' => true,
'standardize_not_equals' => true,
'statement_indentation' => true,
'static_lambda' => true,
'strict_comparison' => true,
'strict_param' => true,
'string_length_to_empty' => true,
'string_line_ending' => true,
'switch_case_semicolon_to_colon' => true,
'switch_case_space' => true,
'switch_continue_to_break' => true,
'ternary_operator_spaces' => true,
'ternary_to_elvis_operator' => true,
'ternary_to_null_coalescing' => true,
'trailing_comma_in_multiline' => [
'after_heredoc' => true,
'elements' => ['arrays'],
],
'trim_array_spaces' => true,
'types_spaces' => ['space' => 'none'],
'unary_operator_spaces' => false,
'use_arrow_functions' => true,
'visibility_required' => ['elements' => ['const', 'method', 'property']],
'void_return' => false, // changes method signature
'whitespace_after_comma_in_array' => true,
'yoda_style' => [
'equal' => false,
'identical' => null,
'less_and_greater' => false,
'always_move_variable' => false,
],
]);

View File

@ -4,21 +4,14 @@ install:
- composer install --ignore-platform-reqs
php:
- 7.3
- 7.4
- 8.0
- 8.1
- nightly
script:
- mkdir -p build/logs
- php vendor/bin/phpunit -c build
#after_script:
# - CODECLIMATE_REPO_TOKEN=2cbddcebcb9256b3402867282e119dbe61de0b31039325356af3c7d72ed6d058 vendor/bin/test-reporter
matrix:
allow_failures:
- php: nightly
#addons:
# code_climate:
# repo_token: 2cbddcebcb9256b3402867282e119dbe61de0b31039325356af3c7d72ed6d058
#matrix:
# allow_failures:
# - php: nightly

View File

@ -1,8 +1,28 @@
# Changelog
## Version 5.3
* Update PHP requirement to 8.2
## Version 5.2
* Updated PHP requirement to 8.1
* Updated to support PHP 8.2
* Improve Anilist <-> Kitsu mappings to be more reliable
## Version 5.1
* Added session check, so when coming back to a page, if the session is expired, the page will refresh.
* Updated logging config so that much fewer, much smaller files are generated.
* Updated Kitsu integration to use GraphQL API, reducing a lot of internal complexity.
## Version 5
* Updated PHP requirement to 7.4
* Added anime watching history view
* Added manga reading history view
* Updated anime collection to have more media types
## Version 4.2
* Updated dependencies
* Updated PHP requirement to 7.3
* Added option to automatically set dark mode based on the OS setting
## Version 4.1
* Added optional dark theme

20
Jenkinsfile vendored
View File

@ -10,39 +10,42 @@ pipeline {
sh 'php composer.phar install --ignore-platform-reqs'
}
}
stage('PHP 7.3') {
stage('PHP 8.1') {
agent {
docker {
image 'php:7.3-alpine'
image 'php:8.1-cli-alpine'
args '-u root --privileged'
}
}
steps {
sh 'apk add --no-cache git'
sh 'apk add --no-cache git icu-dev'
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
sh 'php ./vendor/bin/phpunit --colors=never'
}
}
stage('PHP 7.4') {
stage('PHP 8.2') {
agent {
docker {
image 'php:7.4-alpine'
image 'php:8.2-cli-alpine'
args '-u root --privileged'
}
}
steps {
sh 'apk add --no-cache git'
sh 'apk add --no-cache git icu-dev'
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
sh 'php ./vendor/bin/phpunit --colors=never'
}
}
stage('Latest PHP') {
agent {
docker {
image 'php:alpine'
image 'php:cli-alpine'
args '-u root --privileged'
}
}
steps {
sh 'apk add --no-cache git'
sh 'apk add --no-cache git icu-dev'
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
sh 'php ./vendor/bin/phpunit --colors=never'
}
}
@ -55,6 +58,7 @@ pipeline {
cloverReportDir: '',
cloverReportFileName: 'build/logs/clover.xml',
])
junit 'build/logs/junit.xml'
}
}
}

View File

@ -1,9 +1,9 @@
# Hummingbird Anime Client
Update your anime/manga list on Kitsu.io and MyAnimeList.net
Update your anime/manga list on Kitsu.io and Anilist
[![Build Status](https://travis-ci.org/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.org/timw4mail/HummingBirdAnimeClient)
[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshomepage.net/job/timw4mail/HummingBirdAnimeClient/develop)
[![Build Status](https://travis-ci.com/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.com/github/timw4mail/HummingBirdAnimeClient)
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshome.page/job/timw4mail/job/HummingBirdAnimeClient/job/develop/)
[[Hosted Example](https://list.timshomepage.net)]
@ -31,12 +31,18 @@ Update your anime/manga list on Kitsu.io and MyAnimeList.net
### Requirements
* PHP 7.3+
* PDO SQLite or PDO PostgreSQL (For collection tab)
* GD extension for caching images
* PHP 8.2
* ext-dom (For editing the DOM)
* ext-gd (For caching images)
* ext-intl (For time localization)
* ext-json
* ext-mbstring
* ext-pdo
### Highly Recommended
* Redis or Memcached for caching
* PDO SQLite or PDO PostgreSQL (For collection tab)
### Installation

View File

@ -1,316 +0,0 @@
<?php declare(strict_types=1);
use Robo\Tasks;
if ( ! function_exists('glob_recursive'))
{
// Does not support flag GLOB_BRACE
function glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
{
$files = array_merge($files, glob_recursive($dir.'/'.basename($pattern), $flags));
}
return $files;
}
}
/**
* This is project's console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends Tasks {
/**
* Directories used by analysis tools
*
* @var array
*/
protected $taskDirs = [
'build/logs',
'build/pdepend',
'build/phpdox',
];
/**
* Directories to remove with the clean task
*
* @var array
*/
protected $cleanDirs = [
'coverage',
'docs',
'phpdoc',
'build/logs',
'build/phpdox',
'build/pdepend'
];
/**
* Do static analysis tasks
*/
public function analyze(): void
{
$this->prepare();
$this->lint();
$this->phploc(TRUE);
$this->phpcs(TRUE);
$this->phpmd(TRUE);
$this->dependencyReport();
$this->phpcpdReport();
}
/**
* Run all tests, generate coverage, generate docs, generate code statistics
*/
public function build(): void
{
$this->analyze();
$this->coverage();
$this->docs();
}
/**
* Cleanup temporary files
*/
public function clean(): void
{
$cleanFiles = [
'build/humbug.json',
'build/humbug-log.txt',
];
array_map(static function ($file) {
@unlink($file);
}, $cleanFiles);
// So the task doesn't complain,
// make any 'missing' dirs to cleanup
array_map(static function ($dir) {
if ( ! is_dir($dir))
{
`mkdir -p {$dir}`;
}
}, $this->cleanDirs);
$this->_cleanDir($this->cleanDirs);
$this->_deleteDir($this->cleanDirs);
}
/**
* Run unit tests and generate coverage reports
*/
public function coverage(): void
{
$this->_run(['phpdbg -qrr -- vendor/bin/phpunit -c build']);
}
/**
* Generate documentation with phpdox
*/
public function docs(): void
{
$cmd_parts = [
'vendor/bin/phpdox',
];
$this->_run($cmd_parts, ' && ');
}
/**
* Verify that source files are valid
*/
public function lint(): void
{
$files = $this->getAllSourceFiles();
$chunks = array_chunk($files, (int)shell_exec('getconf _NPROCESSORS_ONLN'));
foreach($chunks as $chunk)
{
$this->parallelLint($chunk);
}
}
/**
* Run the phpcs tool
*
* @param bool $report - if true, generates reports instead of direct output
*/
public function phpcs($report = FALSE): void
{
$report_cmd_parts = [
'vendor/bin/phpcs',
'--standard=./build/phpcs.xml',
'--report-checkstyle=./build/logs/phpcs.xml',
];
$normal_cmd_parts = [
'vendor/bin/phpcs',
'--standard=./build/phpcs.xml',
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
public function phpmd($report = FALSE): void
{
$report_cmd_parts = [
'vendor/bin/phpmd',
'./src',
'xml',
'cleancode,codesize,controversial,design,naming,unusedcode',
'--exclude ParallelAPIRequest',
'--reportfile ./build/logs/phpmd.xml'
];
$normal_cmd_parts = [
'vendor/bin/phpmd',
'./src',
'ansi',
'cleancode,codesize,controversial,design,naming,unusedcode',
'--exclude ParallelAPIRequest'
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
/**
* Run the phploc tool
*
* @param bool $report - if true, generates reports instead of direct output
*/
public function phploc($report = FALSE): void
{
// Command for generating reports
$report_cmd_parts = [
'vendor/bin/phploc',
'--count-tests',
'--log-csv=build/logs/phploc.csv',
'--log-xml=build/logs/phploc.xml',
'src',
'tests'
];
// Command for generating direct output
$normal_cmd_parts = [
'vendor/bin/phploc',
'--count-tests',
'src',
'tests'
];
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
$this->_run($cmd_parts);
}
/**
* Create temporary directories
*/
public function prepare(): void
{
array_map([$this, '_mkdir'], $this->taskDirs);
}
/**
* Lint php files and run unit tests
*/
public function test(): void
{
$this->lint();
$this->_run(['phpunit']);
}
/**
* Create pdepend reports
*/
protected function dependencyReport(): void
{
$cmd_parts = [
'vendor/bin/pdepend',
'--jdepend-xml=build/logs/jdepend.xml',
'--jdepend-chart=build/pdepend/dependencies.svg',
'--overview-pyramid=build/pdepend/overview-pyramid.svg',
'src'
];
$this->_run($cmd_parts);
}
/**
* Get the total list of source files, including tests
*
* @return array
*/
protected function getAllSourceFiles(): array
{
$files = array_merge(
glob_recursive('build/*.php'),
glob_recursive('src/*.php'),
glob_recursive('src/**/*.php'),
glob_recursive('tests/*.php'),
glob_recursive('tests/**/*.php'),
glob('*.php')
);
$files = array_filter($files, static function(string $value) {
return strpos($value, '__snapshots__') === FALSE;
});
sort($files);
return $files;
}
/**
* Run php's linter in one parallel task for the passed chunk
*
* @param array $chunk
*/
protected function parallelLint(array $chunk): void
{
$task = $this->taskParallelExec()
->timeout(5)
->printed(FALSE);
foreach($chunk as $file)
{
$task = $task->process("php -l {$file}");
}
$task->run();
}
/**
* Generate copy paste detector report
*/
protected function phpcpdReport(): void
{
$cmd_parts = [
'vendor/bin/phpcpd',
'--log-pmd build/logs/pmd-cpd.xml',
'src'
];
$this->_run($cmd_parts);
}
/**
* Shortcut for joining an array of command arguments
* and then running it
*
* @param array $cmd_parts - command arguments
* @param string $join_on - what to join the command arguments with
*/
protected function _run(array $cmd_parts, $join_on = ' '): void
{
$this->taskExec(implode($join_on, $cmd_parts))->run();
}
}

View File

@ -2,31 +2,32 @@
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2017 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @link https://github.com/timw4mail/HummingBirdAnimeClient
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
use function Aviat\AnimeClient\loadToml;
use function Aviat\AnimeClient\loadConfig;
// ----------------------------------------------------------------------------
// Lower level configuration
//
// You shouldn't generally need to change anything below this line
// ----------------------------------------------------------------------------
$APP_DIR = realpath(__DIR__ . '/../');
$ROOT_DIR = realpath("{$APP_DIR}/../");
$APP_DIR = dirname(__DIR__);
$ROOT_DIR = dirname($APP_DIR);
$tomlConfig = loadToml(__DIR__);
$tomlConfig = loadConfig(__DIR__);
return array_merge($tomlConfig, [
'root' => $ROOT_DIR,
'asset_dir' => "{$ROOT_DIR}/public",
'base_config_dir' => __DIR__,
'config_dir' => "{$APP_DIR}/config",
@ -56,4 +57,4 @@ return array_merge($tomlConfig, [
// Included config files
'routes' => require 'routes.php',
]);
]);

View File

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

View File

@ -2,24 +2,24 @@
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7
* PHP version 8
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2018 Timothy J. Warren
* @copyright 2015 - 2021 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.0
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
use const Aviat\AnimeClient\{
ALPHA_SLUG_PATTERN,
NUM_PATTERN,
SLUG_PATTERN,
DEFAULT_CONTROLLER,
DEFAULT_CONTROLLER_METHOD,
DEFAULT_CONTROLLER
SLUG_PATTERN,
NUM_PATTERN,
};
// -------------------------------------------------------------------------
@ -27,7 +27,18 @@ use const Aviat\AnimeClient\{
//
// Maps paths to controllers and methods
// -------------------------------------------------------------------------
$routes = [
$base_routes = [
// ---------------------------------------------------------------------
// AJAX Routes
// ---------------------------------------------------------------------
'cache_purge' => [
'path' => '/cache_purge',
'action' => 'clearCache',
],
'heartbeat' => [
'path' => '/heartbeat',
'action' => 'heartbeat',
],
// ---------------------------------------------------------------------
// Anime List Routes
// ---------------------------------------------------------------------
@ -40,6 +51,10 @@ $routes = [
'action' => 'add',
'verb' => 'post',
],
'anime.random' => [
'path' => '/anime/details/random',
'action' => 'random',
],
'anime.details' => [
'path' => '/anime/details/{id}',
'action' => 'details',
@ -73,6 +88,10 @@ $routes = [
'action' => 'delete',
'verb' => 'post',
],
'manga.random' => [
'path' => '/manga/details/random',
'action' => 'random',
],
'manga.details' => [
'path' => '/manga/details/{id}',
'action' => 'details',
@ -171,14 +190,14 @@ $routes = [
'character' => [
'path' => '/character/{slug}',
'tokens' => [
'slug' => SLUG_PATTERN
]
'slug' => SLUG_PATTERN,
],
],
'person' => [
'path' => '/people/{id}',
'path' => '/people/{slug}',
'tokens' => [
'id' => SLUG_PATTERN
]
'slug' => SLUG_PATTERN,
],
],
'default_user_info' => [
'path' => '/me',
@ -190,8 +209,8 @@ $routes = [
'controller' => 'user',
'action' => 'about',
'tokens' => [
'username' => '.*?'
]
'username' => '.*?',
],
],
// ---------------------------------------------------------------------
// Default / Shared routes
@ -212,12 +231,8 @@ $routes = [
'controller' => 'images',
'tokens' => [
'type' => SLUG_PATTERN,
'file' => '[a-z0-9\-]+\.[a-z]{3,4}'
]
],
'cache_purge' => [
'path' => '/cache_purge',
'action' => 'clearCache',
'file' => '[a-z0-9\-]+\.[a-z]{3,4}',
],
],
'settings' => [
'path' => '/settings',
@ -240,6 +255,13 @@ $routes = [
'path' => '/logout',
'action' => 'logout',
],
'history' => [
'controller' => 'history',
'path' => '/history/{type}',
'tokens' => [
'type' => SLUG_PATTERN,
],
],
'increment' => [
'path' => '/{controller}/increment',
'action' => 'increment',
@ -269,13 +291,13 @@ $routes = [
'action' => 'edit',
'tokens' => [
'id' => SLUG_PATTERN,
'status' => '([a-zA-Z\-_]|%20)+',
'status' => SLUG_PATTERN,
],
],
'list' => [
'path' => '/{controller}/{type}{/view}',
'path' => '/{controller}/{status}{/view}',
'tokens' => [
'type' => ALPHA_SLUG_PATTERN,
'status' => ALPHA_SLUG_PATTERN,
'view' => ALPHA_SLUG_PATTERN,
],
],
@ -292,15 +314,10 @@ $defaultMap = [
'verb' => 'get',
];
foreach ($routes as &$route)
$routes = [];
foreach ($base_routes as $name => $route)
{
foreach($defaultMap as $key => $val)
{
if ( ! array_key_exists($key, $route))
{
$route[$key] = $val;
}
}
$routes[$name] = array_merge($defaultMap, $route);
}
return $routes;

View File

@ -4,13 +4,11 @@
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.3
* PHP version 8.1
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2020 Timothy J. Warren
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 4.2
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
@ -19,114 +17,131 @@ namespace Aviat\AnimeClient;
use Aura\Html\HelperLocatorFactory;
use Aura\Router\RouterContainer;
use Aura\Session\SessionFactory;
use Aviat\AnimeClient\API\{
Anilist,
Kitsu,
Kitsu\KitsuRequestBuilder
};
use Aviat\AnimeClient\Model;
use Aviat\Banker\Pool;
use Aviat\AnimeClient\API\{Anilist, Kitsu};
use Aviat\AnimeClient\{Component, Model};
use Aviat\Banker\Teller;
use Aviat\Ion\Config;
use Aviat\Ion\Di\Container;
use Aviat\Ion\Di\{Container, ContainerInterface};
use Laminas\Diactoros\ServerRequestFactory;
use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Zend\Diactoros\{Response, ServerRequestFactory};
use Psr\SimpleCache\CacheInterface;
use function Aviat\Ion\_dir;
if ( ! defined('HB_APP_DIR'))
{
define('HB_APP_DIR', __DIR__);
define('ROOT_DIR', dirname(HB_APP_DIR));
define('TEMPLATE_DIR', _dir(HB_APP_DIR, 'templates'));
}
// -----------------------------------------------------------------------------
// Setup DI container
// -----------------------------------------------------------------------------
return static function ($configArray = []) {
return static function (array $configArray = []): Container {
$container = new Container();
// -------------------------------------------------------------------------
// Logging
// -------------------------------------------------------------------------
$LOG_DIR = _dir(HB_APP_DIR, 'logs');
$appLogger = new Logger('animeclient');
$appLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
$anilistRequestLogger = new Logger('anilist-request');
$anilistRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/anilist_request.log', Logger::NOTICE));
$kitsuRequestLogger = new Logger('kitsu-request');
$kitsuRequestLogger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/kitsu_request.log', Logger::NOTICE));
$appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING));
$container->setLogger($appLogger);
$container->setLogger($anilistRequestLogger, 'anilist-request');
$container->setLogger($kitsuRequestLogger, 'kitsu-request');
foreach (['anilist-request', 'kitsu-request', 'kitsu-graphql'] as $channel)
{
$logger = new Logger($channel);
$handler = new RotatingFileHandler(_dir($LOG_DIR, "{$channel}.log"), 2, Logger::WARNING);
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);
$container->setLogger($logger, $channel);
}
// -------------------------------------------------------------------------
// Injected Objects
// -------------------------------------------------------------------------
// Create Config Object
$container->set('config', static function() use ($configArray) {
return new Config($configArray);
});
$container->set('config', static fn () => new Config($configArray));
// Create Cache Object
$container->set('cache', static function($container): Pool {
$container->set('cache', static function (ContainerInterface $container): CacheInterface {
$logger = $container->getLogger();
$config = $container->get('config')->get('cache');
return new Pool($config, $logger);
});
// Create List Cache
return new Teller($config, $logger);
});
// Create Aura Router Object
$container->set('aura-router', static function() {
return new RouterContainer;
});
$container->set('aura-router', static fn () => new RouterContainer());
// Create Html helper Object
$container->set('html-helper', static function($container) {
$htmlHelper = (new HelperLocatorFactory)->newInstance();
$htmlHelper->set('menu', static function() use ($container) {
$menuHelper = new Helper\Menu();
$menuHelper->setContainer($container);
return $menuHelper;
});
$htmlHelper->set('field', static function() use ($container) {
$formHelper = new Helper\Form();
$formHelper->setContainer($container);
return $formHelper;
});
$htmlHelper->set('picture', static function() use ($container) {
$pictureHelper = new Helper\Picture();
$pictureHelper->setContainer($container);
return $pictureHelper;
});
// Create Html helpers
$container->set('html-helper', static function (ContainerInterface $container) {
$htmlHelper = (new HelperLocatorFactory())->newInstance();
$helpers = [
'menu' => Helper\Menu::class,
'field' => Helper\Form::class,
'picture' => Helper\Picture::class,
];
foreach ($helpers as $name => $class)
{
$htmlHelper->set($name, static function () use ($class, $container) {
$helper = new $class();
$helper->setContainer($container);
return $helper;
});
}
return $htmlHelper;
});
// Create Request/Response Objects
$container->set('request', static function() {
return ServerRequestFactory::fromGlobals(
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
});
$container->set('response', static function() {
return new Response;
// Create Component helpers
$container->set('component-helper', static function (ContainerInterface $container) {
$helper = (new HelperLocatorFactory())->newInstance();
$components = [
'animeCover' => Component\AnimeCover::class,
'mangaCover' => Component\MangaCover::class,
'character' => Component\Character::class,
'media' => Component\Media::class,
'tabs' => Component\Tabs::class,
'verticalTabs' => Component\VerticalTabs::class,
];
foreach ($components as $name => $componentClass)
{
$helper->set($name, static function () use ($container, $componentClass) {
$helper = new $componentClass();
$helper->setContainer($container);
return $helper;
});
}
return $helper;
});
// Create Request Object
$container->set('request', static fn () => ServerRequestFactory::fromGlobals(
$GLOBALS['_SERVER'],
$_GET,
$_POST,
$_COOKIE,
$_FILES
));
// Create session Object
$container->set('session', static function() {
return (new SessionFactory())->newInstance($_COOKIE);
});
// Miscellaneous helper methods
$container->set('util', static function($container): Util {
return new Util($container);
});
$container->set('session', static fn () => (new SessionFactory())->newInstance($_COOKIE));
// Models
$container->set('kitsu-model', static function($container): Kitsu\Model {
$requestBuilder = new KitsuRequestBuilder();
$container->set('kitsu-model', static function (ContainerInterface $container): Kitsu\Model {
$requestBuilder = new Kitsu\RequestBuilder($container);
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
$listItem = new Kitsu\ListItem();
@ -139,10 +154,11 @@ return static function ($configArray = []) {
$cache = $container->get('cache');
$model->setCache($cache);
return $model;
});
$container->set('anilist-model', static function($container): Anilist\Model {
$requestBuilder = new Anilist\AnilistRequestBuilder();
$container->set('anilist-model', static function (ContainerInterface $container): Anilist\Model {
$requestBuilder = new Anilist\RequestBuilder($container);
$requestBuilder->setLogger($container->getLogger('anilist-request'));
$listItem = new Anilist\ListItem();
@ -155,41 +171,29 @@ return static function ($configArray = []) {
return $model;
});
$container->set('anime-model', static function($container) {
return new Model\Anime($container);
});
$container->set('manga-model', static function($container) {
return new Model\Manga($container);
});
$container->set('anime-collection-model', static function($container) {
return new Model\AnimeCollection($container);
});
$container->set('manga-collection-model', static function($container) {
return new Model\MangaCollection($container);
});
$container->set('settings-model', static function($container) {
$model = new Model\Settings($container->get('config'));
$container->set('settings-model', static function ($container) {
$model = new Model\Settings($container->get('config'));
$model->setContainer($container);
return $model;
});
$container->setSimple('anime-model', Model\Anime::class);
$container->setSimple('manga-model', Model\Manga::class);
$container->setSimple('anime-collection-model', Model\AnimeCollection::class);
// Miscellaneous Classes
$container->set('auth', static function($container) {
return new Kitsu\Auth($container);
});
$container->set('url-generator', static function($container) {
return new UrlGenerator($container);
});
$container->setSimple('util', Util::class);
$container->setSimple('auth', Kitsu\Auth::class);
$container->setSimple('url-generator', UrlGenerator::class);
$container->setSimple('render-helper', RenderHelper::class);
// -------------------------------------------------------------------------
// Dispatcher
// -------------------------------------------------------------------------
$container->set('dispatcher', static function($container) {
return new Dispatcher($container);
});
$container->setSimple('dispatcher', Dispatcher::class);
return $container;
};
// End of bootstrap.php
// End of bootstrap.php

View File

@ -0,0 +1,6 @@
################################################################################
# Anilist API #
################################################################################
client_id = "your_client_id"
client_secret = "your_client_secret"
username = "user123"

View File

@ -4,7 +4,7 @@
# See https://git.timshomepage.net/aviat/banker for more information
# Available drivers are apcu, memcache, memcached, redis or null
# Available drivers are memcached, redis or null
# Null cache driver means no caching
driver = "redis"

View File

@ -14,6 +14,9 @@ show_anime_collection = true
# do you wish to show the manga collection?
show_manga_collection = false
# what theme would you like to use? light, dark, or auto
theme = "auto"
################################################################################
# Default views and paths
################################################################################

0
app/logs/.gitkeep Normal file → Executable file
View File

View File

@ -1,15 +1,16 @@
<article
class="media"
data-kitsu-id="<?= $item['id'] ?>"
data-mal-id="<?= $item['mal_id'] ?>"
data-kitsu-id="<?= $item['id'] ?>"
data-anilist-id="<?= $item['anilist_id'] ?>"
data-mal-id="<?= $item['mal_id'] ?>"
>
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
<?php endif ?>
<?= $helper->picture("images/anime/{$item['anime']['id']}.webp") ?>
<?= $_->h->img($item['anime']['cover_image'], ['width' => 220, 'loading' => 'lazy']) ?>
<div class="name">
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['anime']['slug']]) ?>">
<span class="canonical"><?= $item['anime']['title'] ?></span>
<?php foreach ($item['anime']['titles'] as $title): ?>
<br/>
@ -18,7 +19,7 @@
</a>
</div>
<div class="table">
<?php if ($item['private'] || $item['rewatching']): ?>
<?php if (isset($item['private']) || isset($item['rewatching'])): ?>
<div class="row">
<?php foreach (['private', 'rewatching'] as $attr): ?>
<?php if ($item[$attr]): ?>
@ -30,7 +31,15 @@
<?php if ($item['rewatched'] > 0): ?>
<div class="row">
<div>Rewatched <?= $item['rewatched'] ?> time(s)</div>
<?php if ($item['rewatched'] == 1): ?>
<div>Rewatched once</div>
<?php elseif ($item['rewatched'] == 2): ?>
<div>Rewatched twice</div>
<?php elseif ($item['rewatched'] == 3): ?>
<div>Rewatched thrice</div>
<?php else: ?>
<div>Rewatched <?= $item['rewatched'] ?> times</div>
<?php endif ?>
</div>
<?php endif ?>
@ -41,7 +50,7 @@
<?php if ($link['meta']['link']): ?>
<a href="<?= $link['link'] ?>"
title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
<?= $helper->img("/public/images/{$link['meta']['image']}", [
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 20,
'height' => 20,
@ -49,7 +58,7 @@
]); ?>
</a>
<?php else: ?>
<?= $helper->img("/public/images/{$link['meta']['image']}", [
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 20,
'height' => 20,
@ -61,11 +70,11 @@
</div>
<?php endif ?>
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed" title="Edit information about this anime" href="<?=
$url->generate('edit', [
$_->urlFromRoute('edit', [
'controller' => 'anime',
'id' => $item['id'],
'status' => $item['watching_status']
@ -83,9 +92,9 @@
</div>
</div>
<div class="row">
<div class="media_type"><?= $escape->html($item['anime']['show_type']) ?></div>
<div class="airing-status"><?= $escape->html($item['airing']['status']) ?></div>
<div class="age-rating"><?= $escape->html($item['anime']['age_rating']) ?></div>
<div class="media_type"><?= $_->escape->html($item['anime']['show_type']) ?></div>
<div class="airing-status"><?= $_->escape->html($item['airing']['status']) ?></div>
<div class="age-rating"><?= $_->escape->html($item['anime']['age_rating']) ?></div>
</div>
</div>
</article>

View File

@ -0,0 +1,6 @@
<article class="<?= $className ?>">
<div class="name">
<a href="<?= $link ?>"><?= $name ?></a>
</div>
<a href="<?= $link ?>"><?= $picture ?></a>
</article>

View File

@ -0,0 +1,68 @@
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
<?php if ($_->isAuthenticated()): ?>
<div class="edit-buttons" hidden>
<button class="plus-one-chapter">+1 Chapter</button>
</div>
<?php endif ?>
<?= $_->h->img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?>
<div class="name">
<a href="<?= $_->urlFromRoute('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $_->escape->html($item['manga']['title']) ?>
<?php foreach($item['manga']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
<div class="table">
<?php if ($_->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed"
title="Edit information about this manga"
href="<?= $_->urlFromRoute('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">
Edit
</a>
</span>
</div>
<?php endif ?>
<div class="row">
<div><?= $item['manga']['type'] ?></div>
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
</div>
<?php if ($item['rereading']): ?>
<div class="row">
<?php foreach(['rereading'] as $attr): ?>
<?php if($item[$attr]): ?>
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
<?php endif ?>
<?php endforeach ?>
</div>
<?php endif ?>
<?php if ($item['reread'] > 0): ?>
<div class="row">
<?php if ($item['reread'] == 1): ?>
<div>Reread once</div>
<?php elseif ($item['reread'] == 2): ?>
<div>Reread twice</div>
<?php elseif ($item['reread'] == 3): ?>
<div>Reread thrice</div>
<?php else: ?>
<div>Reread <?= $item['reread'] ?> times</div>
<?php endif ?>
</div>
<?php endif ?>
<div class="row">
<div class="chapter_completion">
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
</div>
</div>
</div>
</article>

12
app/templates/media.php Normal file
View File

@ -0,0 +1,12 @@
<article class="<?= $className ?>">
<a href="<?= $link ?>"><?= $picture ?></a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>

View File

@ -0,0 +1,5 @@
<section class="<?= $className ?>">
<?php foreach ($data as $tabName => $tabData): ?>
<?= $callback($tabData, $tabName) ?>
<?php endforeach ?>
</section>

32
app/templates/tabs.php Normal file
View File

@ -0,0 +1,32 @@
<div class="tabs">
<?php $i = 0; foreach ($data as $tabName => $tabData): ?>
<?php if ( ! empty($tabData)): ?>
<?php $id = "{$name}-{$i}"; ?>
<input
role='tab'
aria-controls="_<?= $id ?>"
type="radio"
name="<?= $name ?>"
id="<?= $id ?>"
<?= ($i === 0) ? 'checked="checked"' : '' ?>
/>
<label for="<?= $id ?>"><?= ucfirst($tabName) ?></label>
<?php if ($hasSectionWrapper): ?>
<div class="content full-height">
<?php endif ?>
<section
id="_<?= $id ?>"
role="tabpanel"
class="<?= $className ?>"
>
<?= $callback($tabData, $tabName) ?>
</section>
<?php if ($hasSectionWrapper): ?>
</div>
<?php endif ?>
<?php endif ?>
<?php $i++; endforeach ?>
</div>

View File

@ -0,0 +1,25 @@
<div class="vertical-tabs">
<?php $i = 0; ?>
<?php foreach ($data as $tabName => $tabData): ?>
<?php $id = "{$name}-{$i}" ?>
<div class="tab">
<input
type="radio"
role='tab'
aria-controls="_<?= $id ?>"
name="<?= $name ?>"
id="<?= $id ?>"
<?= $i === 0 ? 'checked="checked"' : '' ?>
/>
<label for="<?= $id ?>"><?= $tabName ?></label>
<section
id='_<?= $id ?>'
role="tabpanel"
class="<?= $className ?>"
>
<?= $callback($tabData, $tabName) ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
</div>

View File

@ -1,7 +1,8 @@
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>Add Anime to your List</h2>
<form action="<?= $action_url ?>" method="post">
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
<section>
<div class="cssload-loader" hidden="hidden">
<div class="cssload-inner cssload-one"></div>

View File

@ -1,6 +1,6 @@
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -11,16 +11,16 @@
<?php foreach ($sections as $name => $items): ?>
<?php if (empty($items)): ?>
<section class="status">
<h2><?= $escape->html($name) ?></h2>
<h2><?= $_->escape->html($name) ?></h2>
<h3>There's nothing here!</h3>
</section>
<?php else: ?>
<section class="status">
<h2><?= $escape->html($name) ?></h2>
<h2><?= $_->escape->html($name) ?></h2>
<section class="media-wrap">
<?php foreach($items as $item): ?>
<?php if ($item['private'] && ! $auth->isAuthenticated()) continue; ?>
<?php include __DIR__ . '/cover-item.php' ?>
<?php if ($item['private'] && ! $_->isAuthenticated()) continue; ?>
<?= $_->component->animeCover($item) ?>
<?php endforeach ?>
</section>
</section>

View File

@ -1,8 +1,12 @@
<?php use function Aviat\AnimeClient\getLocalImg; ?>
<?php
use function Aviat\AnimeClient\friendlyTime;
?>
<main class="details fixed">
<section class="flex">
<aside class="info">
<?= $helper->picture("images/anime/{$data['id']}-original.webp") ?>
<?= $_->h->img($data['cover_image'], ['width' => '390']) ?>
<br />
@ -11,20 +15,40 @@
<td class="align-right">Airing Status</td>
<td><?= $data['status'] ?></td>
</tr>
<?php if ( ! empty($data['airDate'])): ?>
<tr>
<td>Original Airing</td>
<td><?= $data['airDate'] ?></td>
</tr>
<?php endif ?>
<tr>
<td>Show Type</td>
<td><?= $data['show_type'] ?></td>
<td><?= (strlen($data['show_type']) > 3) ? ucfirst(strtolower($data['show_type'])) : $data['show_type'] ?></td>
</tr>
<tr>
<td>Episode Count</td>
<td><?= $data['episode_count'] ?? '-' ?></td>
</tr>
<?php if ( ! empty($data['episode_length'])): ?>
<?php if ($data['episode_count'] !== 1): ?>
<tr>
<td>Episode Length</td>
<td><?= $data['episode_length'] ?> minutes</td>
<td>Episode Count</td>
<td><?= $data['episode_count'] ?? '-' ?></td>
</tr>
<?php endif ?>
<?php if (( ! empty($data['episode_length'])) && $data['episode_count'] !== 1): ?>
<tr>
<td>Episode Length</td>
<td><?= friendlyTime($data['episode_length']) ?></td>
</tr>
<?php endif ?>
<?php if (isset($data['total_length'], $data['episode_count']) && $data['total_length'] > 0): ?>
<tr>
<td>Total Length</td>
<td><?= friendlyTime($data['total_length']) ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['age_rating'])): ?>
<tr>
<td>Age Rating</td>
@ -32,6 +56,18 @@
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -40,12 +76,14 @@
</tr>
</table>
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<?php foreach ($data['titles'] as $title): ?>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>
<br />
@ -72,7 +110,7 @@
href="<?= $link['link'] ?>"
title="Stream '<?= $data['title'] ?>' on <?= $link['meta']['name'] ?>"
>
<?= $helper->img("/public/images/{$link['meta']['image']}", [
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
@ -81,7 +119,7 @@
&nbsp;&nbsp;<?= $link['meta']['name'] ?>
</a>
<?php else: ?>
<?= $helper->img("/public/images/{$link['meta']['image']}", [
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
@ -90,8 +128,8 @@
&nbsp;&nbsp;<?= $link['meta']['name'] ?>
<?php endif ?>
</td>
<td><?= implode(', ', $link['subs']) ?></td>
<td><?= implode(', ', $link['dubs']) ?></td>
<td><?= implode(', ', array_map(fn ($sub) => Locale::getDisplayLanguage($sub, 'en'), $link['subs'])) ?></td>
<td><?= implode(', ', array_map(fn ($dub) => Locale::getDisplayLanguage($dub, 'en'), $link['dubs'])) ?></td>
</tr>
<?php endforeach ?>
</tbody>
@ -99,81 +137,69 @@
<?php endif ?>
<?php if ( ! empty($data['trailer_id'])): ?>
<div class="responsive-iframe">
<h4>Trailer</h4>
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen
></iframe>
<h4>Trailer</h4>
<iframe
width="560"
height="315"
role='img'
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
allow="autoplay; encrypted-media"
allowfullscreen
tabindex='0'
title="<?= $data['title'] ?> trailer video"
></iframe>
</div>
<?php endif ?>
</article>
</section>
<?php if (count($data['characters']) > 0): ?>
<section>
<h2>Characters</h2>
<section>
<h2>Characters</h2>
<div class="tabs">
<?php $i = 0 ?>
<?php foreach ($data['characters'] as $role => $list): ?>
<input
type="radio" name="character-types"
id="character-types-<?= $i ?>" <?= ($i === 0) ? 'checked' : '' ?> />
<label for="character-types-<?= $i ?>"><?= ucfirst($role) ?></label>
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($list as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name">
<?= $helper->a($link, $char['name']); ?>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$id}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endforeach ?>
</div>
</section>
<?= $_->component->tabs('character-types', $data['characters'], static function ($characterList, $role)
use ($_) {
$rendered = [];
foreach ($characterList as $id => $character):
if (empty($character['image']))
{
continue;
}
$rendered[] = $_->component->character(
$character['name'],
$_->urlFromRoute('character', ['slug' => $character['slug']]),
$_->h->img($character['image']),
(strtolower($role) !== 'main') ? 'small-character' : 'character'
);
endforeach;
return implode('', array_map('mb_trim', $rendered));
}) ?>
</section>
<?php endif ?>
<?php if (count($data['staff']) > 0): ?>
<section>
<h2>Staff</h2>
<section>
<h2>Staff</h2>
<div class="vertical-tabs">
<?php $i = 0; ?>
<?php foreach ($data['staff'] as $role => $people): ?>
<div class="tab">
<input type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<section class='content media-wrap flex flex-wrap flex-justify-start'>
<?php foreach ($people as $pid => $person): ?>
<article class='character small-person'>
<?php $link = $url->generate('person', ['id' => $person['id']]) ?>
<div class="name">
<a href="<?= $link ?>">
<?= $person['name'] ?>
</a>
</div>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($person['image']['original'] ?? NULL)) ?>
</a>
</article>
<?php endforeach ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
</div>
</section>
<?= $_->component->verticalTabs('staff-role', $data['staff'], static function ($staffList)
use ($_) {
$rendered = [];
foreach ($staffList as $id => $person):
if (empty($person['image']))
{
continue;
}
$rendered[] = $_ ->component->character(
$person['name'],
$_->urlFromRoute('person', ['slug' => $person['slug']]),
$_->h->img($person['image']),
'character small-person',
);
endforeach;
return implode('', array_map('mb_trim', $rendered));
}) ?>
</section>
<?php endif ?>
</main>

View File

@ -1,4 +1,4 @@
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>Edit Anime List Item</h2>
<form action="<?= $action ?>" method="post">
@ -6,9 +6,9 @@
<thead>
<tr>
<th>
<h3><?= $escape->html($item['anime']['title']) ?></h3>
<h3><?= $_->escape->html($item['anime']['title']) ?></h3>
<?php foreach($item['anime']['titles'] as $title): ?>
<h4><?= $escape->html($title) ?></h4>
<h4><?= $_->escape->html($title) ?></h4>
<?php endforeach ?>
</th>
</tr>
@ -16,7 +16,7 @@
<tbody>
<tr>
<td rowspan="9">
<?= $helper->picture("images/anime/{$item['anime']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
<?= $_->h->img($item['anime']['cover_image']) ?>
</td>
</tr>
<tr>
@ -32,7 +32,7 @@
<td>
<select name="watching_status" id="watching_status">
<?php foreach($statuses as $status_key => $status_title): ?>
<option <?php if($item['watching_status'] === $status_key): ?>selected="selected"<?php endif ?>
<option <?php if(strtolower($item['watching_status']) === $status_key): ?>selected="selected"<?php endif ?>
value="<?= $status_key ?>"><?= $status_title ?></option>
<?php endforeach ?>
</select>
@ -70,7 +70,7 @@
<tr>
<td><label for="notes">Notes</label></td>
<td>
<textarea name="notes" id="notes"><?= $escape->html($item['notes']) ?></textarea>
<textarea name="notes" id="notes"><?= $_->escape->html($item['notes']) ?></textarea>
</td>
</tr>
<tr>
@ -87,7 +87,7 @@
</tbody>
</table>
</form>
<form class="js-delete" action="<?= $url->generate('anime.delete') ?>" method="post">
<form class="js-delete" action="<?= $_->urlFromRoute('anime.delete') ?>" method="post">
<fieldset>
<legend>Danger Zone</legend>
<table class="form invisible">

View File

@ -1,6 +1,7 @@
<?php use function Aviat\AnimeClient\colNotEmpty; ?>
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('anime.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -13,30 +14,32 @@
<?php if (empty($items)): ?>
<h3>There's nothing here!</h3>
<?php else: ?>
<?php
$hasNotes = colNotEmpty($items, 'notes');
?>
<table class='media-wrap'>
<thead>
<tr>
<?php if($auth->isAuthenticated()): ?>
<?php if($_->isAuthenticated()): ?>
<td class="no-border">&nbsp;</td>
<?php endif ?>
<th>Title</th>
<th>Airing Status</th>
<th>Score</th>
<th class='numeric'>Score</th>
<th>Type</th>
<th>Progress</th>
<th>Rated</th>
<th colspan="2">Attributes</th>
<th>Notes</th>
<th>Genres</th>
<th class='numeric'>Progress</th>
<th class='rating'>Age Rating</th>
<th>Attributes</th>
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
</tr>
</thead>
<tbody>
<?php foreach($items as $item): ?>
<?php if ($item['private'] && ! $auth->isAuthenticated()) continue; ?>
<?php if ($item['private'] && ! $_->isAuthenticated()) continue; ?>
<tr id="a-<?= $item['id'] ?>">
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<td>
<a class="bracketed" href="<?= $url->generate('edit', [
<a class="bracketed" href="<?= $_->urlFromRoute('edit', [
'controller' => 'anime',
'id' => $item['id'],
'status' => $item['watching_status']
@ -44,12 +47,11 @@
</td>
<?php endif ?>
<td class="align-left justify">
<a href="<?= $url->generate('anime.details', ['id' => $item['anime']['slug']]) ?>">
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['anime']['slug']]) ?>">
<?= $item['anime']['title'] ?>
</a>
<?php foreach ($item['anime']['titles'] as $title): ?>
<br/><?= $title ?>
<?php endforeach ?>
<br />
<?= implode('<br />', $item['anime']['titles']) ?>
</td>
<td><?= $item['airing']['status'] ?></td>
<td><?= $item['user_rating'] ?> / 10 </td>
@ -60,45 +62,46 @@
</td>
<td><?= $item['anime']['age_rating'] ?></td>
<td>
<ul>
<?php if ($item['rewatched'] > 0): ?>
<li>Rewatched <?= $item['rewatched'] ?> time(s)</li>
<?php endif ?>
<?php foreach(['private','rewatching'] as $attr): ?>
<?php if($item[$attr]): ?>
<li><?= ucfirst($attr); ?></li>
<?php endif ?>
<?php endforeach ?>
</ul>
</td>
<td>
<?php foreach($item['anime']['streaming_links'] as $link): ?>
<?php if ($link['meta']['link'] !== FALSE): ?>
<a href="<?= $link['link'] ?>" title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
<?= $helper->picture("images/{$link['meta']['image']}", 'svg', [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
'alt' => "{$link['meta']['name']} logo",
]); ?>
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'small-streaming-logo',
'width' => 25,
'height' => 25,
'alt' => "{$link['meta']['name']} logo",
]) ?>
</a>
<?php else: ?>
<?= $helper->picture("images/{$link['meta']['image']}", 'svg', [
'class' => 'streaming-logo',
'width' => 50,
'height' => 50,
'alt' => "{$link['meta']['name']} logo",
]); ?>
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
'class' => 'small-streaming-logo',
'width' => 25,
'height' => 25,
'alt' => "{$link['meta']['name']} logo",
]) ?>
<?php endif ?>
<?php endforeach ?>
<br />
<ul>
<?php if ($item['rewatched'] > 0): ?>
<?php if ($item['rewatched'] == 1): ?>
<li>Rewatched once</li>
<?php elseif ($item['rewatched'] == 2): ?>
<li>Rewatched twice</li>
<?php elseif ($item['rewatched'] == 3): ?>
<li>Rewatched thrice</li>
<?php else: ?>
<li>Rewatched <?= $item['rewatched'] ?> times</li>
<?php endif ?>
<?php endif ?>
<?php foreach(['private','rewatching'] as $attr): ?>
<?php if($item[$attr]): ?><li><?= ucfirst($attr); ?></li><?php endif ?>
<?php endforeach ?>
</ul>
</td>
<td>
<p><?= $escape->html($item['notes']) ?></p>
</td>
<td class="align-left">
<?php sort($item['anime']->genres) ?>
<?= implode(', ', $item['anime']->genres) ?>
</td>
<?php if ($hasNotes): ?><td><p><?= $_->escape->html($item['notes']) ?></p></td><?php endif ?>
</tr>
<?php endforeach ?>
</tbody>
@ -107,4 +110,4 @@
<?php endforeach ?>
<?php endif ?>
</main>
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>

View File

@ -1,13 +1,13 @@
<?php
use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\Kitsu;
?>
<main class="character-page details fixed">
<section class="flex flex-no-wrap">
<aside>
<?= $helper->picture("images/characters/{$data['id']}-original.webp") ?>
<?= $_->h->img($data['image']) ?>
</aside>
<div>
<h2 class="toph"><?= $data['name'] ?></h2>
@ -26,70 +26,27 @@ use Aviat\AnimeClient\API\Kitsu;
<br />
<hr />
<div class="description">
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
<p><?= nl2br($data['description']) ?></p>
</div>
</div>
</section>
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
<h3>Media</h3>
<div class="tabs">
<?php if ( ! empty($data['media']['anime'])): ?>
<input checked="checked" type="radio" id="media-anime" name="media-tabs" />
<label for="media-anime">Anime</label>
<section class="media-wrap content">
<?php foreach ($data['media']['anime'] as $id => $anime): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $anime['attributes']['slug']]);
$titles = Kitsu::filterTitles($anime['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$id}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php endif ?>
<?= $_->component->tabs('character-media', $data['media'], static function ($media, $mediaType) use ($_) {
$rendered = [];
foreach ($media as $id => $item)
{
$rendered[] = $_->component->media(
array_merge([$item['title']], $item['titles']),
$_->urlFromRoute("{$mediaType}.details", ['id' => $item['slug']]),
$_->h->img(Kitsu::getPosterImage($item), ['width' => 220, 'loading' => 'lazy']),
);
}
<?php if ( ! empty($data['media']['manga'])): ?>
<input type="radio" id="media-manga" name="media-tabs" />
<label for="media-manga">Manga</label>
<section class="media-wrap content">
<?php foreach ($data['media']['manga'] as $id => $manga): ?>
<article class="media">
<?php
$link = $url->generate('manga.details', ['id' => $manga['attributes']['slug']]);
$titles = Kitsu::filterTitles($manga['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/manga/{$id}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php endif ?>
</div>
return implode('', array_map('mb_trim', $rendered));
}, 'media-wrap content') ?>
<?php endif ?>
<section>
@ -115,10 +72,10 @@ use Aviat\AnimeClient\API\Kitsu;
<td>
<article class="character">
<?php
$link = $url->generate('person', ['id' => $c['person']['id']]);
$link = $_->urlFromRoute('person', ['id' => $c['person']['id']]);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($c['person']['image'], TRUE)) ?>
<?= $_->h->img($c['person']['image']) ?>
<div class="name">
<?= $c['person']['name'] ?>
</div>
@ -130,11 +87,11 @@ use Aviat\AnimeClient\API\Kitsu;
<?php foreach ($c['series'] as $series): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $series['attributes']['slug']]);
$link = $_->urlFromRoute('anime.details', ['id' => $series['attributes']['slug']]);
$titles = Kitsu::filterTitles($series['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
<?= $_->h->img(Kitsu::getPosterImage($series['attributes'])) ?>
</a>
<div class="name">
<a href="<?= $link ?>">
@ -158,66 +115,47 @@ use Aviat\AnimeClient\API\Kitsu;
<?php if ( ! empty($vas)): ?>
<h4>Voice Actors</h4>
<div class="tabs">
<?php $i = 0; ?>
<?= $_->component->tabs('character-vas', $vas, static function ($casting) use ($_) {
$castings = [];
foreach ($casting as $id => $c):
$person = $_->component->character(
$c['person']['name'],
$_->urlFromRoute('person', ['slug' => $c['person']['slug']]),
$_->h->img($c['person']['image']['original']['url']),
);
$medias = array_map(fn ($series) => $_->component->media(
array_merge([$series['title']], $series['titles']),
$_->urlFromRoute('anime.details', ['id' => $series['slug']]),
$_->h->img(Kitsu::getPosterImage($series)),
), $c['series']);
$media = implode('', array_map('mb_trim', $medias));
<?php foreach ($vas as $language => $casting): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="character-va<?= $i ?>"
name="character-vas"
/>
<label for="character-va<?= $i ?>"><?= $language ?></label>
<section class="content">
<table class="borderless max-table">
<tr>
<th>Cast Member</th>
<th>Series</th>
</tr>
<?php foreach ($casting as $cid => $c): ?>
<tr>
<td>
<article class="character">
<?php
$link = $url->generate('person', ['id' => $c['person']['id']]);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($c['person']['image'])) ?>
<div class="name">
<?= $c['person']['name'] ?>
</div>
</a>
</article>
</td>
<td width="75%">
<section class="align-left media-wrap-flex">
<?php foreach ($c['series'] as $series): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $series['attributes']['slug']]);
$titles = Kitsu::filterTitles($series['attributes']);
?>
<a href="<?= $link ?>">
<?= $helper->picture(getLocalImg($series['attributes']['posterImage']['small'], TRUE)) ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
</td>
</tr>
<?php endforeach ?>
</table>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>
$castings[] = <<<HTML
<tr>
<td>{$person}</td>
<td width="75%">
<section class="align-left media-wrap-flex">
{$media}
</section>
</td>
</tr>
HTML;
endforeach;
$languages = implode('', array_map('mb_trim', $castings));
return <<<HTML
<table class="borderless max-table">
<thead>
<tr>
<th>Cast Member</th>
<th>Series</th>
</tr>
</thead>
<tbody>{$languages}</tbody>
</table>
HTML;
}, 'content') ?>
<?php endif ?>
<?php endif ?>
</section>

View File

@ -1,14 +1,15 @@
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>Add <?= ucfirst($collection_type) ?> to your Collection</h2>
<form action="<?= $action_url ?>" method="post">
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
<section>
<div class="cssload-loader" hidden="hidden">
<div class="cssload-inner cssload-one"></div>
<div class="cssload-inner cssload-two"></div>
<div class="cssload-inner cssload-three"></div>
</div>
<label for="search">Search for <?= $collection_type ?> by name:&nbsp;&nbsp;&nbsp;&nbsp;<input type="search" id="search" name="search" /></label>
<label for="search-anime-collection">Search for <?= $collection_type ?> by name:&nbsp;&nbsp;&nbsp;&nbsp;<input type="search" id="search-anime-collection" name="search" /></label>
<section id="series-list" class="media-wrap">
</section>
</section>
@ -16,13 +17,9 @@
<table class="invisible form">
<tbody>
<tr>
<td><label for="media_id">Media</label></td>
<td>
<select name="media_id" id="media_id">
<?php foreach($media_items as $id => $name): ?>
<option value="<?= $id ?>"><?= $name ?></option>
<?php endforeach ?>
</select>
<td class="align-right"><label for="media_id">Media</label></td>
<td class='align-left'>
<?php include 'media-select-list.php' ?>
</td>
</tr>
<tr>

View File

@ -1,24 +1,26 @@
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
<?= $helper->picture("images/anime/{$item['hummingbird_id']}.webp") ?>
<?= $_->h->picture("images/anime/{$item['hummingbird_id']}.webp") ?>
<div class="name">
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['slug']]) ?>">
<?= $item['title'] ?>
<?= ($item['alternate_title'] != "") ? "<small><br />{$item['alternate_title']}</small>" : ""; ?>
</a>
</div>
<div class="table">
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed"
href="<?= $url->generate($collection_type . '.collection.edit.get', [
href="<?= $_->urlFromRoute($collection_type . '.collection.edit.get', [
'id' => $item['hummingbird_id']
]) ?>">Edit</a>
</span>
</div>
<?php endif ?>
<div class="row">
<?php if ($item['episode_count'] > 1): ?>
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
<?php endif ?>
<div class="media_type"><?= $item['show_type'] ?></div>
<div class="age-rating"><?= $item['age_rating'] ?></div>
</div>

View File

@ -1,6 +1,7 @@
<?php use function Aviat\AnimeClient\renderTemplate; ?>
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -8,33 +9,18 @@
<br />
<label>Filter: <input type='text' class='media-filter' /></label>
<br />
<div class="tabs">
<?php $i = 0; ?>
<?php foreach ($sections as $name => $items): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>" name="collection-tabs" />
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<section class="media-wrap">
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/cover-item.php'; ?>
<?php endforeach ?>
</section>
</div>
<?php $i++; ?>
<?php endforeach ?>
<!-- All Tab -->
<input type='radio' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class='content full-height'>
<?php foreach ($sections as $name => $items): ?>
<h3><?= $name ?></h3>
<section class="media-wrap">
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/cover-item.php'; ?>
<?php endforeach ?>
</section>
<?php endforeach ?>
</div>
</div>
<?= $_->component->tabs('collection-tab', $sections, static function ($items) use ($_, $collection_type) {
$rendered = [];
foreach ($items as $item)
{
$rendered[] = renderTemplate(__DIR__ . '/cover-item.php', [
'_' => $_,
'collection_type' => $collection_type,
'item' => $item,
]);
}
return implode('', array_map('mb_trim', $rendered));
}, 'media-wrap', true) ?>
<?php endif ?>
</main>

View File

@ -1,4 +1,5 @@
<?php if ($auth->isAuthenticated()): ?>
<?php use function Aviat\AnimeClient\renderTemplate ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>Edit Anime Collection Item</h2>
<form action="<?= $action_url ?>" method="post">
@ -6,7 +7,7 @@
<tbody>
<tr>
<td rowspan="6" class="align-center">
<?= $helper->picture("images/anime/{$item['hummingbird_id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
<?= $_->h->picture("images/anime/{$item['hummingbird_id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
</td>
</tr>
<tr>
@ -24,16 +25,12 @@
<tr>
<td class="align-right"><label for="media_id">Media</label></td>
<td class="align-left">
<select name="media_id" id="media_id">
<?php foreach($media_items as $id => $name): ?>
<option <?= $item['media_id'] === $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
<?php endforeach ?>
</select>
<?php include 'media-select-list.php' ?>
</td>
</tr>
<tr>
<td><label for="notes">Notes</label></td>
<td><textarea id="notes" name="notes"><?= $escape->html($item['notes']) ?></textarea></td>
<td><textarea id="notes" name="notes"><?= $_->escape->html($item['notes']) ?></textarea></td>
</tr>
<tr>
<td>&nbsp;</td>
@ -47,7 +44,7 @@
</tbody>
</table>
</form>
<form class="js-delete" action="<?= $url->generate($collection_type . '.collection.delete') ?>" method="post">
<form class="js-delete" action="<?= $_->urlFromRoute($collection_type . '.collection.delete') ?>" method="post">
<fieldset>
<legend>Danger Zone</legend>
<table class="form invisible">

View File

@ -1,20 +1,23 @@
<tr>
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<td>
<a class="bracketed"
href="<?= $url->generate($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
href="<?= $_->urlFromRoute($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
</td>
<?php endif ?>
<td class="align-left">
<a href="<?= $url->generate('anime.details', ['id' => $item['slug']]) ?>">
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['slug']]) ?>">
<?= $item['title'] ?>
</a>
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
</td>
<td><?= $item['episode_count'] ?></td>
<?php if ($hasMedia): ?>
<td><?= implode(', ', $item['media']) ?></td>
<?php endif ?>
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
<td><?= $item['episode_length'] ?></td>
<td><?= $item['show_type'] ?></td>
<td><?= $item['age_rating'] ?></td>
<?php if ($hasNotes): ?><td class="align-left"><?= nl2br($item['notes'] ?? '', TRUE) ?></td><?php endif ?>
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
<td class="align-left"><?= $item['notes'] ?></td>
</tr>

View File

@ -1,6 +1,7 @@
<?php use function Aviat\AnimeClient\{colNotEmpty, renderTemplate}; ?>
<main>
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate($collection_type . '.collection.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -8,67 +9,47 @@
<br />
<label>Filter: <input type='text' class='media-filter' /></label>
<br />
<?php $i = 0; ?>
<div class="tabs">
<?php foreach ($sections as $name => $items): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="collection-tab-<?= $i ?>"
name="collection-tabs"/>
<label for="collection-tab-<?= $i ?>"><h2><?= $name ?></h2></label>
<div class="content full-height">
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>Actions</td>
<?php endif ?>
<th>Title</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<th>Genres</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/list-item.php' ?>
<?php endforeach ?>
</tbody>
</table>
</div>
<?php $i++ ?>
<?php endforeach ?>
<!-- All -->
<input type='radio' id='collection-tab-<?= $i ?>' name='collection-tabs' />
<label for='collection-tab-<?= $i ?>'><h2>All</h2></label>
<div class="content full-height">
<?php foreach ($sections as $name => $items): ?>
<h3><?= $name ?></h3>
<?= $_->component->tabs('collection-tab', $sections, static function ($items, $section) use ($_, $helper, $collection_type) {
$hasNotes = colNotEmpty($items, 'notes');
$hasMedia = $section === 'All';
$firstTh = ($_->isAuthenticated()) ? '<td>&nbsp;</td>' : '';
$mediaTh = ($hasMedia) ? '<th>Media</th>' : '';
$noteTh = ($hasNotes) ? '<th>Notes</th>' : '';
$rendered = [];
foreach ($items as $item)
{
$rendered[] = renderTemplate(__DIR__ . '/list-item.php', [
'_' => $_,
'collection_type' => $collection_type,
'hasMedia' => $hasMedia,
'hasNotes' => $hasNotes,
'helper' => $helper,
'item' => $item,
]);
}
$rows = implode('', array_map('mb_trim', $rendered));
return <<<HTML
<table class="full-width media-wrap">
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<td>Actions</td>
<?php endif ?>
<th>Title</th>
<th>Episode Count</th>
<th>Episode Length</th>
<th>Show Type</th>
<th>Age Rating</th>
<th>Genres</th>
<th>Notes</th>
</tr>
<tr>
{$firstTh}
<th>Title</th>
{$mediaTh}
<th class='numeric'>Episode Count</th>
<th class='numeric'>Episode Length</th>
<th>Show Type</th>
<th class='rating'>Age Rating</th>
{$noteTh}
<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<?php include __DIR__ . '/list-item.php' ?>
<?php endforeach ?>
</tbody>
<tbody>{$rows}</tbody>
</table>
<?php endforeach; ?>
</div>
</div>
HTML;
}) ?>
<?php endif ?>
</main>
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>

View File

@ -0,0 +1,11 @@
<select name="media_id[]" id="media_id" multiple size="13">
<?php foreach ($media_items as $group => $items): ?>
<optgroup label='<?= $group ?>'>
<?php foreach ($items as $id => $name): ?>
<option <?= in_array($id, ($item['media_id'] ?? []), FALSE) ? 'selected="selected"' : '' ?> value="<?= $id ?>">
<?= $name ?>
</option>
<?php endforeach ?>
</optgroup>
<?php endforeach ?>
</select>

View File

@ -11,12 +11,6 @@
</div>
</section>
<script nomodule="nomodule" src="https://polyfill.io/v3/polyfill.min.js?features=es5%2CObject.assign"></script>
<?php if ($auth->isAuthenticated()): ?>
<script nomodule='nomodule' async="async" defer="defer" src="<?= $urlGenerator->assetUrl('js/scripts-authed.min.js') ?>"></script>
<script type="module" src="<?= $urlGenerator->assetUrl('js/src/index-authed.js') ?>"></script>
<?php else: ?>
<script nomodule="nomodule" async="async" defer="defer" src="<?= $urlGenerator->assetUrl('js/scripts.min.js') ?>"></script>
<script type="module" src="<?= $urlGenerator->assetUrl('js/src/index.js') ?>"></script>
<?php endif ?>
<script async="async" defer="defer" src="<?= $_->assetUrl('js/scripts.min.js') ?>"></script>
</body>
</html>

View File

@ -6,29 +6,25 @@
<meta http-equiv="cache-control" content="no-store" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=1" />
<?php if ($config->get('theme') !== 'auto'): ?>
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/app.min.css') ?>" />
<?php elseif ($config->get('theme') === 'auto'): ?>
<link rel="stylesheet" href="<?= $urlGenerator->assetUrl('css/dark-auto.min.css') ?>" />
<?php endif ?>
<link rel="<?= $config->get('theme') === 'dark' ? '' : 'alternate ' ?>stylesheet" title="Dark Theme" href="<?= $urlGenerator->assetUrl('css/dark.min.css') ?>" />
<link rel="icon" href="<?= $urlGenerator->assetUrl('images/icons/favicon.ico') ?>" />
<link rel="apple-touch-icon" sizes="57x57" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-57x57.png') ?>">
<link rel="apple-touch-icon" sizes="60x60" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-60x60.png') ?>">
<link rel="apple-touch-icon" sizes="72x72" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-72x72.png') ?>">
<link rel="apple-touch-icon" sizes="76x76" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-76x76.png') ?>">
<link rel="apple-touch-icon" sizes="114x114" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-114x114.png') ?>">
<link rel="apple-touch-icon" sizes="120x120" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-120x120.png') ?>">
<link rel="apple-touch-icon" sizes="144x144" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-144x144.png') ?>">
<link rel="apple-touch-icon" sizes="152x152" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-152x152.png') ?>">
<link rel="apple-touch-icon" sizes="180x180" href="<?= $urlGenerator->assetUrl('images/icons/apple-icon-180x180.png') ?>">
<link rel="icon" type="image/png" sizes="192x192" href="<?= $urlGenerator->assetUrl('images/icons/android-icon-192x192.png') ?>">
<link rel="icon" type="image/png" sizes="32x32" href="<?= $urlGenerator->assetUrl('images/icons/favicon-32x32.png') ?>">
<link rel="icon" type="image/png" sizes="96x96" href="<?= $urlGenerator->assetUrl('images/icons/favicon-96x96.png') ?>">
<link rel="icon" type="image/png" sizes="16x16" href="<?= $urlGenerator->assetUrl('images/icons/favicon-16x16.png') ?>">
<link rel="stylesheet" href="<?= $_->assetUrl('css/' . $_->config->get('theme') . '.min.css') ?>" />
<link rel="<?= $_->config->get('theme') === 'dark' ? '' : 'alternate ' ?>stylesheet" title="Dark Theme" href="<?= $_->assetUrl('css/dark.min.css') ?>" />
<link rel="icon" href="<?= $_->assetUrl('images/icons/favicon.ico') ?>" />
<link rel="apple-touch-icon" sizes="57x57" href="<?= $_->assetUrl('images/icons/apple-icon-57x57.png') ?>">
<link rel="apple-touch-icon" sizes="60x60" href="<?= $_->assetUrl('images/icons/apple-icon-60x60.png') ?>">
<link rel="apple-touch-icon" sizes="72x72" href="<?= $_->assetUrl('images/icons/apple-icon-72x72.png') ?>">
<link rel="apple-touch-icon" sizes="76x76" href="<?= $_->assetUrl('images/icons/apple-icon-76x76.png') ?>">
<link rel="apple-touch-icon" sizes="114x114" href="<?= $_->assetUrl('images/icons/apple-icon-114x114.png') ?>">
<link rel="apple-touch-icon" sizes="120x120" href="<?= $_->assetUrl('images/icons/apple-icon-120x120.png') ?>">
<link rel="apple-touch-icon" sizes="144x144" href="<?= $_->assetUrl('images/icons/apple-icon-144x144.png') ?>">
<link rel="apple-touch-icon" sizes="152x152" href="<?= $_->assetUrl('images/icons/apple-icon-152x152.png') ?>">
<link rel="apple-touch-icon" sizes="180x180" href="<?= $_->assetUrl('images/icons/apple-icon-180x180.png') ?>">
<link rel="icon" type="image/png" sizes="192x192" href="<?= $_->assetUrl('images/icons/android-icon-192x192.png') ?>">
<link rel="icon" type="image/png" sizes="32x32" href="<?= $_->assetUrl('images/icons/favicon-32x32.png') ?>">
<link rel="icon" type="image/png" sizes="96x96" href="<?= $_->assetUrl('images/icons/favicon-96x96.png') ?>">
<link rel="icon" type="image/png" sizes="16x16" href="<?= $_->assetUrl('images/icons/favicon-16x16.png') ?>">
</head>
<body class="<?= $escape->attr($url_type) ?> list">
<body class="<?= $_->escape->attr($url_type) ?> list">
<?php include 'setup-check.php' ?>
<header>
<?php
@ -43,5 +39,4 @@
}
}
?>
</header>

47
app/views/history.php Normal file
View File

@ -0,0 +1,47 @@
<main class="details fixed">
<?php if (empty($items)): ?>
<h3>No recent history.</h3>
<?php else: ?>
<section>
<?php foreach ($items as $name => $item): ?>
<article class="flex flex-no-wrap flex-justify-start">
<section class="flex-self-center history-img">
<a href="<?= $item['url'] ?>">
<?= $_->h->img(
$item['coverImg'],
['width' => '110px', 'height' => '156px'],
) ?>
</a>
</section>
<section class="flex-self-center">
<?= $_->h->a($item['url'], $item['title']) ?>
<br />
<br />
<?= $item['action'] ?>
<br />
<small>
<?php if ( ! empty($item['dateRange'])):
[$startDate, $endDate] = array_map(
fn ($date) => $date->format('l, F d'),
$item['dateRange']
);
[$startTime, $endTime] = array_map(
fn ($date) => $date->format('h:i:s A'),
$item['dateRange']
);
?>
<?php if ($startDate === $endDate): ?>
<?= "{$startDate}, {$startTime} &ndash; {$endTime}" ?>
<?php else: ?>
<?= "{$startDate} {$startTime} &ndash; {$endDate} {$endTime}" ?>
<?php endif ?>
<?php else: ?>
<?= $item['updated']->format('l, F d h:i:s A') ?>
<?php endif ?>
</small>
</section>
</article>
<?php endforeach ?>
</section>
<?php endif ?>
</main>

6
app/views/js-warning.php Normal file
View File

@ -0,0 +1,6 @@
<noscript>
<div class="message error">
<span class="icon"></span>
This feature requires Javascript to function :(
</div>
</noscript>

View File

@ -1,7 +1,7 @@
<main>
<h2><?= $config->get('whose_list'); ?>'s Login</h2>
<h2><?= $_->config->get('whose_list'); ?>'s Login</h2>
<?= $message ?>
<form method="post" action="<?= $url->generate('login.post') ?>">
<form method="post" action="<?= $_->urlFromRoute('login.post') ?>">
<table class="form invisible">
<tr>
<td><label for="password">Password: </label></td>

View File

@ -1,88 +1,114 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 8.1
*
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5.2
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient;
$whose = $config->get('whose_list') . "'s ";
$lastSegment = $urlGenerator->lastSegment();
$whose = $_->config->get('whose_list') . "'s ";
$lastSegment = $_->lastSegment();
$extraSegment = $lastSegment === 'list' ? '/list' : '';
$hasAnime = stripos($_SERVER['REQUEST_URI'], 'anime') !== FALSE;
$hasManga = stripos($_SERVER['REQUEST_URI'], 'manga') !== FALSE;
$hasAnime = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'anime');
$hasManga = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'manga');
?>
<div id="main-nav" class="flex flex-align-end flex-wrap">
<span class="flex-no-wrap grow-1">
<?php if(strpos($route_path, 'collection') === FALSE): ?>
<?= $helper->a(
$urlGenerator->defaultUrl($url_type),
$whose . ucfirst($url_type) . ' List'
<?php if( ! str_contains($route_path, 'collection')): ?>
<?= $_->h->a(
$_->defaultUrl($url_type),
$whose . ucfirst($url_type) . ' List',
['aria-current'=> 'page']
) ?>
<?php if($config->get("show_{$url_type}_collection")): ?>
[<?= $helper->a(
$url->generate("{$url_type}.collection.view") . $extraSegment,
<?php if($_->config->get("show_{$url_type}_collection")): ?>
[<?= $_->h->a(
$_->urlFromRoute("{$url_type}.collection.view") . $extraSegment,
ucfirst($url_type) . ' Collection'
) ?>]
<?php endif ?>
<?php if($config->get("show_{$other_type}_collection")): ?>
[<?= $helper->a(
$url->generate("{$other_type}.collection.view") . $extraSegment,
<?php if($_->config->get("show_{$other_type}_collection")): ?>
[<?= $_->h->a(
$_->urlFromRoute("{$other_type}.collection.view") . $extraSegment,
ucfirst($other_type) . ' Collection'
) ?>]
<?php endif ?>
[<?= $helper->a(
$urlGenerator->defaultUrl($other_type) . $extraSegment,
[<?= $_->h->a(
$_->defaultUrl($other_type) . $extraSegment,
ucfirst($other_type) . ' List'
) ?>]
<?php else: ?>
<?= $whose . ucfirst($url_type) . ' Collection' ?>
<?php if($config->get("show_{$other_type}_collection")): ?>
[<?= $helper->a(
$url->generate("{$other_type}.collection.view") . $extraSegment,
<?= $_->h->a(
$_->urlFromRoute("{$url_type}.collection.view") . $extraSegment,
$whose . ucfirst($url_type) . ' Collection',
['aria-current'=> 'page']
) ?>
<?php if($_->config->get("show_{$other_type}_collection")): ?>
[<?= $_->h->a(
$_->urlFromRoute("{$other_type}.collection.view") . $extraSegment,
ucfirst($other_type) . ' Collection'
) ?>]
<?php endif ?>
[<?= $helper->a($urlGenerator->defaultUrl('anime') . $extraSegment, 'Anime List') ?>]
[<?= $helper->a($urlGenerator->defaultUrl('manga') . $extraSegment, 'Manga List') ?>]
[<?= $_->h->a($_->defaultUrl('anime') . $extraSegment, 'Anime List') ?>]
[<?= $_->h->a($_->defaultUrl('manga') . $extraSegment, 'Manga List') ?>]
<?php endif ?>
<?php if ($auth->isAuthenticated() && $config->get(['cache', 'driver']) !== 'null'): ?>
<?php if ($_->isAuthenticated() && $_->config->get(['cache', 'driver']) !== 'null'): ?>
<span class="flex-no-wrap small-font">
<button type="button" class="js-clear-cache user-btn">Clear API Cache</button>
</span>
<?php endif ?>
</span>
<span class="flex-no-wrap small-font">[<?= $helper->a(
$url->generate('default_user_info'),
'About '. $config->get('whose_list')
<span class="flex-no-wrap small-font">[<?= $_->h->a(
$_->urlFromRoute('default_user_info'),
'About '. $_->config->get('whose_list')
) ?>]</span>
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<span class="flex-no-wrap small-font">
<?= $helper->a(
$url->generate('settings'),
<?= $_->h->a(
$_->urlFromRoute('settings'),
'Settings',
['class' => 'bracketed']
) ?>
</span>
<span class="flex-no-wrap small-font">
<?= $helper->a(
$url->generate('logout'),
<?= $_->h->a(
$_->urlFromRoute('logout'),
'Logout',
['class' => 'bracketed']
) ?>
</span>
<?php else: ?>
<span class="flex-no-wrap small-font">
[<?= $helper->a($url->generate('login'), "{$whose} Login") ?>]
[<?= $_->h->a($_->urlFromRoute('login'), "{$whose} Login") ?>]
</span>
<?php endif ?>
</div>
<?php if ($_->isViewPage() && ($hasAnime || $hasManga)): ?>
<nav>
<?php if ($container->get('util')->isViewPage() && ($hasAnime || $hasManga)): ?>
<?= $helper->menu($menu_name) ?>
<?= $_->h->menu($menu_name) ?>
<?php if (stripos($GLOBALS['_SERVER']['REQUEST_URI'], 'history') === FALSE): ?>
<br />
<ul>
<li class="<?= Util::isNotSelected('list', $lastSegment) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
<li class="<?= Util::isSelected('list', $lastSegment) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
<?php $currentView = Util::eq('list', $lastSegment) ? 'list' : 'cover' ?>
<li class="<?= Util::isNotSelected('list', $lastSegment) ?>">
<a aria-current="<?= Util::ariaCurrent($currentView === 'cover') ?>"
href="<?= $_->urlFromPath($route_path) ?>">Cover View</a>
</li>
<li class="<?= Util::isSelected('list', $lastSegment) ?>">
<a aria-current="<?= Util::ariaCurrent($currentView === 'list') ?>"
href="<?= $_->urlFromPath("{$route_path}/list") ?>">List View</a>
</li>
</ul>
<?php endif ?>
<?php endif ?>
</nav>
<?php endif ?>

View File

@ -1,7 +1,8 @@
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>Add Manga to your List</h2>
<form action="<?= $action_url ?>" method="post">
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
<section>
<div class="cssload-loader" hidden="hidden">
<div class="cssload-inner cssload-one"></div>

View File

@ -1,6 +1,6 @@
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -11,80 +11,15 @@
<?php foreach ($sections as $name => $items): ?>
<?php if (empty($items)): ?>
<section class="status">
<h2><?= $escape->html($name) ?></h2>
<h2><?= $_->escape->html($name) ?></h2>
<h3>There's nothing here!</h3>
</section>
<?php else: ?>
<section class="status">
<h2><?= $escape->html($name) ?></h2>
<h2><?= $_->escape->html($name) ?></h2>
<section class="media-wrap">
<?php foreach($items as $item): ?>
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
<?php if ($auth->isAuthenticated()): ?>
<div class="edit-buttons" hidden>
<button class="plus-one-chapter">+1 Chapter</button>
<?php /* <button class="plus-one-volume">+1 Volume</button> */ ?>
</div>
<?php endif ?>
<?= $helper->picture("images/manga/{$item['manga']['id']}.webp") ?>
<div class="name">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $escape->html($item['manga']['title']) ?>
<?php foreach($item['manga']['titles'] as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
<div class="table">
<?php if ($auth->isAuthenticated()): ?>
<div class="row">
<span class="edit">
<a class="bracketed"
title="Edit information about this manga"
href="<?= $url->generate('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
]) ?>">
Edit
</a>
</span>
</div>
<?php endif ?>
<div class="row">
<div><?= $item['manga']['type'] ?></div>
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
</div>
<?php if ($item['rereading']): ?>
<div class="row">
<?php foreach(['rereading'] as $attr): ?>
<?php if($item[$attr]): ?>
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
<?php endif ?>
<?php endforeach ?>
</div>
<?php endif ?>
<?php if ($item['reread'] > 0): ?>
<div class="row">
<div>Reread <?= $item['reread'] ?> time(s)</div>
</div>
<?php endif ?>
<div class="row">
<div class="chapter_completion">
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
</div>
<?php /* </div>
<div class="row"> */ ?>
<div class="volume_completion">
Volumes: <span class="volume_count"><?= $item['volumes']['total'] ?></span>
</div>
</div>
</div>
</article>
<?= $component->mangaCover($item, $name) ?>
<?php endforeach ?>
</section>
</section>

View File

@ -1,23 +1,51 @@
<main class="details fixed">
<section class="flex flex-no-wrap">
<aside class="info">
<?= $helper->picture("images/manga/{$data['id']}-original.webp", 'jpg', ['class' => 'cover']) ?>
<?= $_->h->img($data['cover_image'], ['class' => 'cover', 'width' => '350']) ?>
<br />
<table class="media-details">
<tr>
<td>Manga Type</td>
<td><?= ucfirst($data['manga_type']) ?></td>
<td class="align-right">Publishing Status</td>
<td><?= $data['status'] ?></td>
</tr>
<tr>
<td>Manga Type</td>
<td><?= ucfirst(strtolower($data['manga_type'])) ?></td>
</tr>
<?php if ( ! empty($data['volume_count'])): ?>
<tr>
<td>Volume Count</td>
<td><?= $data['volume_count'] ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['chapter_count'])): ?>
<tr>
<td>Chapter Count</td>
<td><?= $data['chapter_count'] ?></td>
</tr>
<?php endif ?>
<?php if ( ! empty($data['age_rating'])): ?>
<tr>
<td>Age Rating</td>
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
</td>
</tr>
<?php endif ?>
<?php if (count($data['links']) > 0): ?>
<tr>
<td>External Links</td>
<td>
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
<?php endforeach ?>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -29,8 +57,8 @@
<br />
</aside>
<article class="text">
<h2 class="toph"><a rel="external" href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
<?php foreach ($data['titles'] as $title): ?>
<h2 class="toph"><?= $data['title'] ?></h2>
<?php foreach ($data['titles_more'] as $title): ?>
<h3><?= $title ?></h3>
<?php endforeach ?>
@ -44,61 +72,34 @@
<?php if (count($data['characters']) > 0): ?>
<h2>Characters</h2>
<div class="tabs">
<?php $i = 0 ?>
<?php foreach ($data['characters'] as $role => $list): ?>
<input
type="radio" name="character-role-tabs"
id="character-tabs<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="character-tabs<?= $i ?>"><?= ucfirst($role) ?></label>
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($list as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="<?= $role === 'supporting' ? 'small-' : '' ?>character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name">
<?= $helper->a($link, $char['name']); ?>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$id}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>
<?= $component->tabs('manga-characters', $data['characters'], static function($list, $role) use ($component, $helper, $_) {
$rendered = [];
foreach ($list as $id => $char)
{
$rendered[] = $component->character(
$char['name'],
$_->urlFromRoute('character', ['slug' => $char['slug']]),
$_->h->img($char['image'], ['loading' => 'lazy']),
($role !== 'main') ? 'small-character' : 'character'
);
}
return implode('', array_map('mb_trim', $rendered));
}) ?>
<?php endif ?>
<?php if (count($data['staff']) > 0): ?>
<h2>Staff</h2>
<div class="vertical-tabs">
<?php $i = 0 ?>
<?php foreach ($data['staff'] as $role => $people): ?>
<div class="tab">
<input
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<section class='content media-wrap flex flex-wrap flex-justify-start'>
<?php foreach ($people as $pid => $person): ?>
<article class='character person'>
<?php $link = $url->generate('person', ['id' => $pid]) ?>
<div class="name">
<a href="<?= $link ?>">
<?= $person['name'] ?>
</a>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/people/{$pid}.webp") ?>
</a>
</article>
<?php endforeach ?>
</section>
</div>
<?php $i++ ?>
<?php endforeach ?>
</div>
<?= $component->verticalTabs('manga-staff', $data['staff'],
fn($people) => implode('', array_map(
fn ($person) => $component->character(
$person['name'],
$_->urlFromRoute('person', ['slug' => $person['slug']]),
$_->h->img($person['image']),
),
$people
))
) ?>
<?php endif ?>
</main>

View File

@ -1,4 +1,4 @@
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<main>
<h2>
Edit Manga List Item
@ -8,9 +8,9 @@
<thead>
<tr>
<th>
<h3><?= $escape->html($item['manga']['title']) ?></h3>
<h3><?= $_->escape->html($item['manga']['title']) ?></h3>
<?php foreach ($item['manga']['titles'] as $title): ?>
<h4><?= $escape->html($title) ?></h4>
<h4><?= $_->escape->html($title) ?></h4>
<?php endforeach ?>
</th>
</tr>
@ -18,7 +18,7 @@
<tbody>
<tr>
<td rowspan="9">
<?= $helper->picture("images/manga/{$item['manga']['id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
<?= $_->h->img($item['manga']['image']) ?>
</td>
</tr>
<tr>
@ -46,13 +46,6 @@
value="<?= $item['chapters']['read'] ?>"/> / <?= $item['chapters']['total'] ?>
</td>
</tr>
<tr>
<td><label for="volumes_read">Volumes Read</label></td>
<td>
<?php /*<input type="number" disabled="disabled" min="0" name="volumes_read" id="volumes_read" value="" /> */ ?>
- / <?= $item['volumes']['total'] ?>
</td>
</tr>
<tr>
<td><label for="rereading_flag">Rereading?</label></td>
<td>
@ -71,7 +64,7 @@
<tr>
<td><label for="notes">Notes</label></td>
<td>
<textarea name="notes" id="notes"><?= $escape->html($item['notes']) ?></textarea>
<textarea name="notes" id="notes"><?= $_->escape->html($item['notes']) ?></textarea>
</td>
</tr>
<tr>
@ -90,7 +83,7 @@
</form>
<fieldset>
<legend>Danger Zone</legend>
<form class="js-delete" action="<?= $url->generate('manga.delete') ?>" method="post">
<form class="js-delete" action="<?= $_->urlFromRoute('manga.delete') ?>" method="post">
<table class="form invisible">
<tbody>
<tr>

View File

@ -1,6 +1,6 @@
<main class="media-list">
<?php if ($auth->isAuthenticated()): ?>
<a class="bracketed" href="<?= $url->generate('manga.add.get') ?>">Add Item</a>
<?php if ($_->isAuthenticated()): ?>
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
<?php endif ?>
<?php if (empty($sections)): ?>
<h3>There's nothing here!</h3>
@ -16,24 +16,22 @@
<table class='media-wrap'>
<thead>
<tr>
<?php if ($auth->isAuthenticated()): ?>
<?php if ($_->isAuthenticated()): ?>
<td>&nbsp;</td>
<?php endif ?>
<th>Title</th>
<th>Rating</th>
<th>Completed Chapters</th>
<th># of Volumes</th>
<th class='numeric'>Score</th>
<th class='numeric'>Completed Chapters</th>
<th>Attributes</th>
<th>Type</th>
<th>Genres</th>
</tr>
</thead>
<tbody>
<?php foreach($items as $item): ?>
<tr id="manga-<?= $item['id'] ?>">
<?php if($auth->isAuthenticated()): ?>
<?php if($_->isAuthenticated()): ?>
<td>
<a class="bracketed" href="<?= $url->generate('edit', [
<a class="bracketed" href="<?= $_->urlFromRoute('edit', [
'controller' => 'manga',
'id' => $item['id'],
'status' => $name
@ -41,7 +39,7 @@
</td>
<?php endif ?>
<td class="align-left">
<a href="<?= $url->generate('manga.details', ['id' => $item['manga']['slug']]) ?>">
<a href="<?= $_->urlFromRoute('manga.details', ['id' => $item['manga']['slug']]) ?>">
<?= $item['manga']['title'] ?>
</a>
<?php foreach($item['manga']['titles'] as $title): ?>
@ -50,11 +48,16 @@
</td>
<td><?= $item['user_rating'] ?> / 10</td>
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>
<td><?= $item['volumes']['total'] ?></td>
<td>
<ul>
<?php if ($item['reread'] > 0): ?>
<li>Reread <?= $item['reread'] ?> time(s)</li>
<?php if ($item['reread'] == 1): ?>
<li>Reread once</li>
<?php elseif ($item['reread'] == 2): ?>
<li>Reread twice</li>
<?php elseif ($item['reread'] == 3): ?>
<li>Reread thrice</li>
<?php elseif ($item['reread'] > 3): ?>
<li>Reread <?= $item['reread'] ?> times</li>
<?php endif ?>
<?php foreach(['rereading'] as $attr): ?>
<?php if($item[$attr]): ?>
@ -64,9 +67,6 @@
</ul>
</td>
<td><?= $item['manga']['type'] ?></td>
<td class="align-left">
<?= implode(', ', $item['manga']['genres']) ?>
</td>
</tr>
<?php endforeach ?>
</tbody>
@ -75,4 +75,4 @@
<?php endforeach ?>
<?php endif ?>
</main>
<script defer="defer" src="<?= $urlGenerator->assetUrl('js/tables.min.js') ?>"></script>
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>

View File

@ -1,5 +1,5 @@
<div class="message <?= $escape->attr($message_type) ?>">
<div class="message <?= $_->escape->attr($message_type) ?>">
<span class="icon"></span>
<?= $escape->html($message) ?>
<?= $_->escape->html($message) ?>
<span class="close"></span>
</div>

View File

@ -1,67 +0,0 @@
<?php
use function Aviat\AnimeClient\getLocalImg;
use Aviat\AnimeClient\API\Kitsu;
?>
<h3>Voice Acting Roles</h3>
<div class="tabs">
<?php $i = 0; ?>
<?php foreach($data['characters'] as $role => $characterList): ?>
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" name="character-type-tabs" id="character-type-<?= $i ?>" />
<label for="character-type-<?= $i ?>"><h5><?= ucfirst($role) ?></h5></label>
<section class="content">
<table class="borderless max-table">
<tr>
<th>Character</th>
<th>Series</th>
</tr>
<?php foreach ($characterList as $cid => $character): ?>
<tr>
<td>
<article class="character">
<?php
$link = $url->generate('character', ['slug' => $character['character']['slug']]);
?>
<a href="<?= $link ?>">
<?php $imgPath = ($character['character']['image'] === NULL)
? 'images/characters/empty.png'
: getLocalImg($character['character']['image']['original']);
echo $helper->picture($imgPath);
?>
<div class="name">
<?= $character['character']['canonicalName'] ?>
</div>
</a>
</article>
</td>
<td>
<section class="align-left media-wrap">
<?php foreach ($character['media'] as $sid => $series): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $series['slug']]);
$titles = Kitsu::filterTitles($series);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$sid}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
</td>
</tr>
<?php endforeach; ?>
</table>
</section>
<?php $i++ ?>
<?php endforeach ?>
</div>

View File

@ -1,21 +1,28 @@
<?php
use Aviat\AnimeClient\API\Kitsu;
?>
<main class="details fixed">
<section class="flex flex-no-wrap">
<div>
<?= $helper->picture("images/people/{$data['id']}-original.webp", 'jpg', ['class' => 'cover' ]) ?>
<?= $_->h->img($data['image'], ['class' => 'cover' ]) ?>
</div>
<div>
<h2 class="toph"><?= $data['name'] ?></h2>
<?php foreach ($data['names'] as $name): ?>
<h3><?= $name ?></h3>
<?php endforeach ?>
<?php if ( ! empty($data['birthday'])): ?>
<h4><?= $data['birthday'] ?></h4>
<?php endif ?>
<br />
<hr />
<div class="description">
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
</div>
</div>
</section>
<?php if ( ! empty($data['staff'])): ?>
<section>
<h3>Castings</h3>
<div class="vertical-tabs">
<?php $i = 0 ?>
<?php foreach ($data['staff'] as $role => $entries): ?>
@ -24,31 +31,17 @@ use Aviat\AnimeClient\API\Kitsu;
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<?php foreach ($entries as $type => $casting): ?>
<?php if ($type === 'characters') continue; ?>
<?php if ( ! (empty($entries['manga']) || empty($entries['anime']))): ?>
<?php if (isset($entries['manga'], $entries['anime'])): ?>
<h4><?= ucfirst($type) ?></h4>
<?php endif ?>
<section class="content">
<section class="content media-wrap flex flex-wrap flex-justify-start">
<?php foreach ($casting as $sid => $series): ?>
<article class="media">
<?php
$mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime';
$link = $url->generate("{$mediaType}.details", ['id' => $series['slug']]);
$titles = Kitsu::filterTitles($series);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/{$type}/{$sid}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br />
<small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
<?= $_->component->media(
$series['titles'],
$_->urlFromRoute("{$mediaType}.details", ['id' => $series['slug']]),
$_->h->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
) ?>
<?php endforeach; ?>
</section>
<?php endforeach ?>
@ -59,9 +52,53 @@ use Aviat\AnimeClient\API\Kitsu;
</section>
<?php endif ?>
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
<?php if ( ! empty($data['characters'])): ?>
<section>
<?php include 'character-mapping.php' ?>
<h3>Voice Acting Roles</h3>
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $_) {
$voiceRoles = [];
foreach ($characterList as $cid => $item):
$character = $component->character(
$item['character']['canonicalName'],
$_->urlFromRoute('character', ['slug' => $item['character']['slug']]),
$_->h->img($item['character']['image'], ['loading' => 'lazy']),
);
$medias = [];
foreach ($item['media'] as $sid => $series)
{
$medias[] = $component->media(
$series['titles'],
$_->urlFromRoute('anime.details', ['id' => $series['slug']]),
$_->h->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
);
}
$media = implode('', array_map('mb_trim', $medias));
$voiceRoles[] = <<<HTML
<tr>
<td>{$character}</td>
<td>
<section class="align-left media-wrap">{$media}</section>
</td>
</tr>
HTML;
endforeach;
$roles = implode('', array_map('mb_trim', $voiceRoles));
return <<<HTML
<table class="borderless max-table">
<thead>
<tr>
<th>Character</th>
<th>Series</th>
</tr>
</thead>
<tbody>{$roles}</tbody>
</table>
HTML;
}) ?>
</section>
<?php endif ?>
</main>

View File

@ -0,0 +1,24 @@
<?php if ( ! $hasRequiredAnilistConfig): ?>
<p class="static-message info">See the <a href="https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wiki/anilist">wiki</a> to learn how to set up Anilist integration. </p>
<?php else: ?>
<?php $auth = $anilistModel->checkAuth(); ?>
<?php if (array_key_exists('errors', $auth)): ?>
<p class="static-message error">Anilist API Client is Not Authorized.</p>
<?= $_->h->a(
$_->urlFromRoute('anilist-redirect'),
'Link Anilist Account',
['class' => 'bracketed user-btn']
) ?>
<?php else: ?>
<?php $expires = $_->config->get(['anilist', 'access_token_expires']); ?>
<p class="static-message info">
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
</p>
<?php require __DIR__ . '/_form.php' ?>
<?= $_->h->a(
$_->urlFromRoute('anilist-redirect'),
'Update Access Token',
['class' => 'bracketed user-btn']
) ?>
<?php endif ?>
<?php endif ?>

View File

@ -0,0 +1,5 @@
<article>
<label for="<?= $fieldName ?>"><?= $field['title'] ?></label><br />
<small><?= $field['description'] ?></small><br />
<?= $_->h->field($fieldName, $field); ?>
</article>

View File

@ -6,19 +6,19 @@
?>
<?php foreach ($fields as $name => $field): ?>
<?php $fieldname = ($section === 'config' || $nestedPrefix !== 'config') ? "{$nestedPrefix}[{$name}]" : "{$nestedPrefix}[{$section}][{$name}]"; ?>
<?php
$fieldName = ($section === 'config' || $nestedPrefix !== 'config')
? "{$nestedPrefix}[{$name}]"
: "{$nestedPrefix}[{$section}][{$name}]";
?>
<?php if ($field['type'] === 'subfield'): ?>
<section>
<h4><?= $field['title'] ?></h4>
<?php include_once '_form.php'; ?>
<?php include '_subfield.php'; ?>
</section>
<?php elseif ( ! empty($field['display'])): ?>
<article>
<label for="<?= $fieldname ?>"><?= $field['title'] ?></label><br />
<small><?= $field['description'] ?></small><br />
<?= $helper->field($fieldname, $field); ?>
</article>
<?php include '_field.php' ?>
<?php else: ?>
<?php $hiddenFields[] = $helper->field($fieldname, $field); ?>
<?php $hiddenFields[] = $_->h->field($fieldName, $field); ?>
<?php endif ?>
<?php endforeach ?>

View File

@ -0,0 +1,20 @@
<?php
// Higher scoped variables:
// $field
// $fields
// $hiddenFields
// $nestedPrefix
?>
<?php foreach ($field['fields'] as $name => $field): ?>
<?php
$fieldName = ($section === 'config' || $nestedPrefix !== 'config')
? "{$nestedPrefix}[{$name}]"
: "{$nestedPrefix}[{$section}][{$name}]";
?>
<?php if ( ! empty($field['display'])): ?>
<?php include '_field.php' ?>
<?php else: ?>
<?php $hiddenFields[] = $_->h->field($fieldName, $field); ?>
<?php endif ?>
<?php endforeach ?>

View File

@ -1,5 +1,5 @@
<?php
if ( ! $auth->isAuthenticated())
if ( ! $_->isAuthenticated())
{
echo '<h1>Not Authorized</h1>';
return;
@ -16,7 +16,7 @@ $hiddenFields = [];
$nestedPrefix = 'config';
?>
<form action="<?= $url->generate('settings-post') ?>" method="POST">
<form action="<?= $_->urlFromRoute('settings-post') ?>" method="POST">
<main class='settings form'>
<button type="submit">Save Changes</button>
<div class="tabs">
@ -28,27 +28,11 @@ $nestedPrefix = 'config';
/>
<label for="settings-tab<?= $i ?>"><h3><?= $sectionMapping[$section] ?></h3></label>
<section class="content">
<?php require __DIR__ . '/_form.php' ?>
<?php if ($section === 'anilist'): ?>
<hr />
<?php $auth = $anilistModel->checkAuth(); ?>
<?php if (array_key_exists('errors', $auth)): ?>
<p class="static-message error">Not Authorized.</p>
<?= $helper->a(
$url->generate('anilist-redirect'),
'Link Anilist Account'
) ?>
<?php else: ?>
<?php $expires = $config->get(['anilist', 'access_token_expires']); ?>
<p class="static-message info">
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
</p>
<?= $helper->a(
$url->generate('anilist-redirect'),
'Update Access Token'
) ?>
<?php endif ?>
<?php endif ?>
<?php
($section === 'anilist')
? require __DIR__ . '/_anilist.php'
: require __DIR__ . '/_form.php'
?>
</section>
<?php $i++; ?>
<?php endforeach ?>
@ -60,7 +44,3 @@ $nestedPrefix = 'config';
<button type="submit">Save Changes</button>
</main>
</form>

View File

@ -2,7 +2,7 @@
use function Aviat\AnimeClient\checkFolderPermissions;
$setupErrors = checkFolderPermissions($container->get('config'));
$setupErrors = checkFolderPermissions($_->config);
?>
<?php if ( ! empty($setupErrors)): ?>

View File

@ -1,42 +1,60 @@
<?php
use Aviat\AnimeClient\API\Kitsu;
use Aviat\AnimeClient\Kitsu;
?>
<main class="user-page details">
<h2 class="toph">
<?= $helper->a(
"https://kitsu.io/users/{$data['slug']}",
$data['name'], [
'title' => 'View profile on Kitsu'
About
<?= $_->h->a(
"https://kitsu.io/users/{$data['slug']}",
$data['name'], [
'title' => 'View profile on Kitsu'
])
?>
</h2>
<p><?= $escape->html($data['about']) ?></p>
<section class="flex flex-no-wrap">
<aside class="info">
<center>
<?= $helper->img($urlGenerator->assetUrl($data['avatar']), ['alt' => '']); ?>
</center>
<table class="media-details invisible">
<tr>
<?php if($data['avatar'] !== null): ?>
<td><?= $_->h->img($data['avatar'], ['alt' => '', 'width' => '225']); ?></td>
<?php endif ?>
<td><?= $_->escape->html($data['about']) ?></td>
</tr>
</table>
<br />
<table class="media-details">
<?php foreach ([
'joinDate' => 'Joined',
'birthday' => 'Birthday',
'gender' => 'Gender',
'location' => 'Location'
] as $key => $label): ?>
<?php if ($data[$key] !== null): ?>
<tr>
<td>Location</td>
<td><?= $data['location'] ?></td>
<td><?= $label ?></td>
<td><?= $data[$key] ?></td>
</tr>
<?php endif ?>
<?php endforeach; ?>
<?php if ($data['website'] !== null): ?>
<tr>
<td>Website</td>
<td><?= $helper->a($data['website'], $data['website']) ?></td>
<td><?= $_->h->a($data['website'], $data['website']) ?></td>
</tr>
<?php if ( ! empty($data['waifu'])): ?>
<?php endif ?>
<?php if ($data['waifu']['character'] !== null): ?>
<tr>
<td><?= $escape->html($data['waifu']['label']) ?></td>
<td><?= $_->escape->html($data['waifu']['label']) ?></td>
<td>
<?php
$character = $data['waifu']['character'];
echo $helper->a(
$url->generate('character', ['slug' => $character['slug']]),
$character['canonicalName']
echo $_->component->character(
$character['names']['canonical'],
$_->urlFromRoute('character', ['slug' => $character['slug']]),
$_->h->img(Kitsu::getImage($character))
);
?>
</td>
@ -57,79 +75,35 @@ use Aviat\AnimeClient\API\Kitsu;
<article>
<?php if ( ! empty($data['favorites'])): ?>
<h3>Favorites</h3>
<div class="tabs">
<?php $i = 0 ?>
<?php if ( ! empty($data['favorites']['characters'])): ?>
<input type="radio" name="user-favorites" id="user-fav-chars" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-chars">Characters</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['characters'] as $id => $char): ?>
<?php if ( ! empty($char['image']['original'])): ?>
<article class="character">
<?php $link = $url->generate('character', ['slug' => $char['slug']]) ?>
<div class="name"><?= $helper->a($link, $char['canonicalName']); ?></div>
<a href="<?= $link ?>">
<?= $helper->picture("images/characters/{$char['id']}.webp") ?>
</a>
</article>
<?php endif ?>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
<?php if ( ! empty($data['favorites']['anime'])): ?>
<input type="radio" name="user-favorites" id="user-fav-anime" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-anime">Anime</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['anime'] as $anime): ?>
<article class="media">
<?php
$link = $url->generate('anime.details', ['id' => $anime['slug']]);
$titles = Kitsu::filterTitles($anime);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/anime/{$anime['id']}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
<?php if ( ! empty($data['favorites']['manga'])): ?>
<input type="radio" name="user-favorites" id="user-fav-manga" <?= $i === 0 ? 'checked' : '' ?> />
<label for="user-fav-manga">Manga</label>
<section class="content full-width media-wrap">
<?php foreach($data['favorites']['manga'] as $manga): ?>
<article class="media">
<?php
$link = $url->generate('manga.details', ['id' => $manga['slug']]);
$titles = Kitsu::filterTitles($manga);
?>
<a href="<?= $link ?>">
<?= $helper->picture("images/manga/{$manga['id']}.webp") ?>
</a>
<div class="name">
<a href="<?= $link ?>">
<?= array_shift($titles) ?>
<?php foreach ($titles as $title): ?>
<br /><small><?= $title ?></small>
<?php endforeach ?>
</a>
</div>
</article>
<?php endforeach ?>
</section>
<?php $i++; ?>
<?php endif ?>
</div>
<?= $_->component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($_) {
if ($type === 'character')
{
uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
}
else
{
uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
}
$rendered = array_map(fn ($item) => match ($type) {
'character' => $_->component->character(
$item['names']['canonical'],
$_->urlFromRoute('character', ['slug' => $item['slug']]),
$_->h->img(Kitsu::getImage($item))
),
default => $_->component->media(
array_merge(
[$item['titles']['canonical']],
Kitsu::getFilteredTitles($item['titles']),
),
$_->urlFromRoute("{$type}.details", ['id' => $item['slug']]),
$_->h->img(Kitsu::getPosterImage($item), ['width' => 220]),
),
}, $items);
return implode('', array_map('mb_trim', $rendered));
}, 'content full-width media-wrap') ?>
<?php endif ?>
</article>
</section>

View File

@ -1,28 +0,0 @@
<documentation title="Closing comments instead of PHP closing tag">
<standard>
<![CDATA[
The PHP closing tag on a PHP document ?> is optional to the PHP parser. However, if used, any whitespace following the closing tag, whether introduced by the developer, user, or an FTP application, can cause unwanted output, PHP errors, or if the latter are suppressed, blank pages. For this reason, all PHP files should OMIT the closing PHP tag, and instead use a comment block to mark the end of file and it's location relative to the application root. This allows you to still identify a file as being complete and not truncated.
]]>
</standard>
<code_comparison>
<code title="Examples of valid closing comments">
<![CDATA[
<?php
echo "Here's my code!";
/* End of file myfile.php */
/* Location: ./system/modules/mymodule/myfile.php */
]]>
</code>
<code title="Examples of invalid closing comments">
<![CDATA[
<?php
echo "Here's my code!";
?>
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,7 +0,0 @@
<documentation title="Unicode (UTF-8) encoding without BOM">
<standard>
<![CDATA[
Files should be saved with Unicode (UTF-8) encoding. The BOM should not be used. Unlike UTF-16 and UTF-32, there's no byte order to indicate in a UTF-8 encoded file, and the BOM can have a negative side effect in PHP of sending output, preventing the application from being able to set its own headers. Unix line endings should be used (LF).
]]>
</standard>
</documentation>

View File

@ -1,31 +0,0 @@
<documentation title="Constructor Names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
]]>
</standard>
<code_comparison>
<code title="Examples of valid constructor name">
<![CDATA[
class Super_class
{
function Super_class()
{
echo 'Some code here !';
}
}
]]>
</code>
<code title="Examples of invalid constructor name">
<![CDATA[
class Super_class
{
function __constructor()
{
echo 'Some code here !';
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,21 +0,0 @@
<documentation title="Class names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
]]>
</standard>
<code_comparison>
<code title="Examples of valid class names">
<![CDATA[
class Super_class
]]>
</code>
<code title="Examples of invalid class names">
<![CDATA[
class SuperClass // words not separated with underscores and words next to the first one start with an upper case
class superclass // words not separated with underscores
class Super_Class // words next to the first one start with an upper case
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,21 +0,0 @@
<documentation title="File names">
<standard>
<![CDATA[
To be able to find which class is contained in a file, file names should be case-insensitively equal to class names. Some operating systems and tools are case-insensitive, though other are. So, file names should be in lower case to avoid any trouble.
]]>
</standard>
<code_comparison>
<code title="Examples of valid file names">
<![CDATA[
super_class.php
]]>
</code>
<code title="Examples of invalid file names">
<![CDATA[
superclass.php // words not separated with underscores
SuperClass.php // not in lower case and words not separated with underscores
Super_class.php // not in lower case
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,27 +0,0 @@
<documentation title="Function and Method Names">
<standard>
<![CDATA[
Class names should always start with an uppercase letter. Multiple words should be separated with an underscore, and not CamelCased. All other class methods should be entirely lowercased and named to clearly indicate their function, preferably including a verb. Try to avoid overly long and verbose names.
Methods and variables that are only accessed internally by your class, such as utility and helper functions that your public methods use for code abstraction, should be prefixed with an underscore.
]]>
</standard>
<code_comparison>
<code title="Examples of valid method names">
<![CDATA[
function get_file_properties() // descriptive, underscore separator, and all lowercase letters
private function _get_file_properties()
]]>
</code>
<code title="Examples of invalid method names">
<![CDATA[
function fileproperties() // not descriptive and needs underscore separator
function fileProperties() // not descriptive and uses CamelCase
function getfileproperties() // Better! But still missing underscore separator
function getFileProperties() // uses CamelCase
function get_the_file_properties_from_the_file() // wordy
private function get_the_file_properties() // not prefixed with an underscor, though private
function _get_the_file_properties() // prefixed with an underscor, though public
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,31 +0,0 @@
<documentation title="Variable names">
<standard>
<![CDATA[
Namely, variables should contain only lowercase letters, use underscore separators, and be reasonably named to indicate their purpose and contents. Very short, non-word variables should only be used as iterators in for() loops.
Methods and variables that are only accessed internally by your class, such as utility and helper functions that your public methods use for code abstraction, should be prefixed with an underscore.
]]>
</standard>
<code_comparison>
<code title="Examples of valid variable names">
<![CDATA[
for ($j = 0; $j < 10; $j++)
$str
$buffer
$group_id
$last_city
private $_internal_data;
]]>
</code>
<code title="Examples of invalid variable names">
<![CDATA[
$j = 'foo'; // single letter variables should only be used in for() loops
$Str // contains uppercase letters
$bufferedText // uses CamelCasing, and could be shortened without losing semantic meaning
$groupid // multiple words, needs underscore separator
$name_of_last_city_used // too long
private $internal_data; // not prefixed with an underscor, though private
$_public_attribute; // prefixed with an underscor, though public
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,40 +0,0 @@
<documentation title="Strict comparison operators">
<standard>
<![CDATA[
Some PHP functions return FALSE on failure, but may also have a valid return value of "" or 0, which would evaluate to FALSE in loose comparisons. Be explicit by comparing the variable type when using these return values in conditionals to ensure the return value is indeed what you expect, and not a value that has an equivalent loose-type evaluation.
Use the same stringency in returning and checking your own variables. Use === and !== as necessary.
]]>
</standard>
<code_comparison>
<code title="Valid strict comparison">
<![CDATA[
if (strpos($str, 'foo') === FALSE) {
echo 'Do something.';
}
function build_string($str = "")
{
if ($str === "") {
echo 'Buid string.';
}
}
]]>
</code>
<code title="Invalid loose comparison">
<![CDATA[
// If 'foo' is at the beginning of the string, strpos will return a 0,
// resulting in this conditional evaluating as TRUE
if (strpos($str, 'foo') == FALSE) {
echo 'Do something.';
}
function build_string($str = "")
{
if ($str == "") { // uh-oh! What if FALSE or the integer 0 is passed as an argument?
echo 'Buid string.';
}
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,28 +0,0 @@
<documentation title="Double-quoted strings">
<standard>
<![CDATA[
Always use single quoted strings unless you need variables parsed, and in cases where you do need variables parsed, use braces to prevent greedy token parsing. You may also use double-quoted strings if the string contains single quotes, so you do not have to use escape characters.
]]>
</standard>
<code_comparison>
<code title="Examples of invalid double-quoted strings">
<![CDATA[
"My String" // no variable parsing, so no use for double quotes
"My string $foo" // needs braces
'SELECT foo FROM bar WHERE baz = \'bag\'' // ugly
'\r\n' // it isn't wrong, but it won't be interpreted as a new line feed
]]>
</code>
<code title="Examples of valid double-quoted strings">
<![CDATA[
'My String'
"My string {$foo}" // variables in strings may be enclosed with braces in 2 ways
"My string ${foo}"
"My string {$foo['bar']}" // variables in strings may be an array entry
"My string {$foo->bar}" // variables in strings may be an object attribute
"SELECT foo FROM bar WHERE baz = 'bag'"
"\n" // not specified in Code Igniter coding standard, but it should be allowed
]]>
</code>
</code_comparison>
</documentation>

View File

@ -1,187 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Commenting_InlineCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Commenting_InlineCommentSniff.
*
* Ensure the use of single line comments within code (i.e //)
* and blank lines between large comment blocks and code.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Commenting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class InlineCommentSniff implements Sniff
{
/**
* @var int Limit defining long comments.
* Long comments count $longCommentLimit or more lines.
*/
public $longCommentLimit = 5;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_COMMENT
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// keep testing only if it's about the first comment of the block
$previousCommentPtr = $phpcsFile->findPrevious($tokens[$stackPtr]['code'], $stackPtr - 1);
if ($tokens[$previousCommentPtr]['line'] !== $tokens[$stackPtr]['line'] - 1) {
if (TRUE !== $this->_checkCommentStyle($phpcsFile, $stackPtr)) {
return;
}
$commentLines = $this->_getCommentBlock($phpcsFile, $stackPtr);
if (count($commentLines) >= $this->longCommentLimit) {
$this->_checkBlankLinesAroundLongComment($phpcsFile, $commentLines);
}
}
}//end process()
/**
* Add error to $phpcsFile, if comment pointed by $stackPtr doesn't start
* with '//'.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* that has to be a comment.
*
* @return bool TRUE if the content of the token pointed by $stackPtr starts
* with //, FALSE if an error was added to $phpcsFile.
*/
private function _checkCommentStyle(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$stackPtr]['content'][0] === '#') {
$error = 'Perl-style comments are not allowed; use "// Comment" or DocBlock comments instead';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
} else if (substr($tokens[$stackPtr]['content'], 0, 2) === '/*'
|| $tokens[$stackPtr]['content'][0] === '*'
) {
$error = 'Multi lines comments are not allowed; use "// Comment" DocBlock comments instead';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
} else if (substr($tokens[$stackPtr]['content'], 0, 2) !== '//') {
$error = 'Use single line or DocBlock comments within code';
$phpcsFile->addError($error, $stackPtr, 'WrongStyle');
return FALSE;
}
return TRUE;
}//_checkCommentStyle()
/**
* Gather into an array all comment lines to which $stackPtr belongs.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr Pointer to the first comment line.
*
* @return type array Pointers to tokens making up the comment block.
*/
private function _getCommentBlock(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$commentLines = array($stackPtr);
$nextComment = $stackPtr;
$lastLine = $tokens[$stackPtr]['line'];
while (($nextComment = $phpcsFile->findNext($tokens[$stackPtr]['code'], ($nextComment + 1), null, false)) !== false) {
if (($tokens[$nextComment]['line'] - 1) !== $lastLine) {
// Not part of the block.
break;
}
$lastLine = $tokens[$nextComment]['line'];
$commentLines[] = $nextComment;
}
return $commentLines;
}//_getCommentBlock()
/**
* Add errors to $phpcsFile, if $commentLines isn't enclosed with blank lines.
*
* @param File $phpcsFile The current file being scanned.
* @param array $commentLines Lines of the comment block being checked.
*
* @return bool TRUE if $commentLines is enclosed with at least a blank line
* before and after, FALSE otherwise.
*/
private function _checkBlankLinesAroundLongComment(File $phpcsFile, array $commentLines)
{
$hasBlankLinesAround = TRUE;
$tokens = $phpcsFile->getTokens();
// check blank line before the long comment
$firstCommentPtr = reset($commentLines);
$firstPreviousSpacePtr = $firstCommentPtr - 1;
while (T_WHITESPACE === $tokens[$firstPreviousSpacePtr]['code'] && $firstPreviousSpacePtr > 0) {
$firstPreviousSpacePtr--;
}
if ($tokens[$firstPreviousSpacePtr]['line'] >= $tokens[$firstCommentPtr]['line'] - 1) {
$error = "Please add a blank line before comments counting more than {$this->longCommentLimit} lines.";
$phpcsFile->addError($error, $firstCommentPtr, 'LongCommentWithoutSpacing');
$hasBlankLinesAround = FALSE;
}
// check blank line after the long comment
$lastCommentPtr = end($commentLines);
$lastNextSpacePtr = $lastCommentPtr + 1;
while (T_WHITESPACE === $tokens[$lastNextSpacePtr]['code'] && $lastNextSpacePtr < count($tokens)) {
$lastNextSpacePtr++;
}
if ($tokens[$lastNextSpacePtr]['line'] <= $tokens[$lastCommentPtr]['line'] + 1) {
$error = "Please add a blank line after comments counting more than {$this->longCommentLimit} lines.";
$phpcsFile->addError($error, $lastCommentPtr, 'LongCommentWithoutSpacing');
$hasBlankLinesAround = FALSE;
}
return $hasBlankLinesAround;
}//end _checkBlanksAroundLongComment()
}//end class
?>

View File

@ -1,98 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ByteOrderMarkSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ByteOrderMarkSniff.
*
* Ensures that no BOM appears at the beginning of file.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ByteOrderMarkSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array( T_OPEN_TAG );
}//end register()
/**
* List of supported BOM definitions.
*
* Use encoding names as keys and hex BOM representations as values.
*
* @return array
*/
protected function getBomDefinitions()
{
return array(
'UTF-8' => 'efbbbf',
'UTF-16 (BE)' => 'feff',
'UTF-16 (LE)' => 'fffe',
'UTF-32 (BE)' => '0000feff',
'UTF-32 (LE)' => 'fffe0000'
);
}//end getBomDefinitions()
/**
* Process tokens.
*
* Actually, only proceed when we're at index 0, this should be the only case
* that will contain BOM. Then check if BOM definition matches what
* we've found as file's inline HTML. Inline HTML could be longer than just BOM
* so make sure you test as much as needed.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr )
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$tokens = $phpcsFile->getTokens();
$fileStartString = $tokens[0]['content'];
foreach ($this->getBomDefinitions() as $bomName => $expectedBomHex) {
$bomByteLength = strlen($expectedBomHex) / 2;
$fileStartHex = bin2hex(substr($fileStartString, 0, $bomByteLength));
if ($fileStartHex === $expectedBomHex) {
$error = "File contains a $bomName byte order mark (BOM).";
$phpcsFile->addError($error, $stackPtr, 123);
break;
}
}
}//end process()
}

View File

@ -1,222 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_Utf8EncodingSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_Utf8EncodingSniff.
*
* Ensures that PHP files are encoded with Unicode (UTF-8) encoding.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class Utf8EncodingSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$file_path = $phpcsFile->getFilename();
$file_name = basename($file_path);
$file_content = file_get_contents($file_path);
if (false === mb_check_encoding($file_content, 'UTF-8')) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding.';
$phpcsFile->addError($error, 0);
}
if ( ! self::_checkUtf8W3c($file_content)) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding, but it did not successfully pass the W3C test.';
$phpcsFile->addError($error, 0);
}
if ( ! self::_checkUtf8Rfc3629($file_content)) {
$error = 'File "' . $file_name . '" should be saved with Unicode (UTF-8) encoding, but it did not meet RFC3629 requirements.';
$phpcsFile->addError($error, 0);
}
}//end process()
/**
* Checks that the string $content contains only valid UTF-8 chars
* using W3C's method.
* Returns true if $content contains only UTF-8 chars, false otherwise.
*
* @param string $content String to check.
*
* @return bool true if $content contains only UTF-8 chars, false otherwise.
*
* @see http://w3.org/International/questions/qa-forms-utf-8.html
*/
private static function _checkUtf8W3c($content)
{
$content_chunks=self::mb_chunk_split($content, 4096, '');
foreach($content_chunks as $content_chunk)
{
$preg_result= preg_match(
'%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs',
$content_chunk
);
if($preg_result!==1)
{
return false;
}
}
return true;
}//end _checkUtf8W3c()
/**
* Checks that the string $content contains only valid UTF-8 chars
* using the method described in RFC 3629.
* Returns true if $content contains only UTF-8 chars, false otherwise.
*
* @param string $content String to check.
*
* @return bool true if $content contains only UTF-8 chars, false otherwise.
*
* @see http://www.php.net/manual/en/function.mb-detect-encoding.php#85294
*/
private static function _checkUtf8Rfc3629($content)
{
$len = strlen($content);
for ($i = 0; $i < $len; $i++) {
$c = ord($content[$i]);
if ($c > 128) {
if (($c >= 254)) {
return false;
} elseif ($c >= 252) {
$bits=6;
} elseif ($c >= 248) {
$bits=5;
} elseif ($c >= 240) {
$bytes = 4;
} elseif ($c >= 224) {
$bytes = 3;
} elseif ($c >= 192) {
$bytes = 2;
} else {
return false;
} if (($i + $bytes) > $len) {
return false;
} while ($bytes > 1) {
$i++;
$b = ord($content[$i]);
if ($b < 128 || $b > 191) {
return false;
}
$bytes--;
}
}
}
return true;
}//_checkUtf8Rfc3629()
/**
* Splits a string to chunks of given size
* This helps to avoid segmentation fault errors when large text is given
* Returns array of strings after splitting
*
* @param string $str String to split.
* @param int $len number of characters per chunk
*
* @return array string array after splitting
*
* @see http://php.net/manual/en/function.chunk-split.php
*/
private static function mb_chunk_split($str, $len, $glue)
{
if (empty($str)) return false;
$array = self::mbStringToArray ($str);
$n = -1;
$new = Array();
foreach ($array as $char) {
$n++;
if ($n < $len) $new []= $char;
elseif ($n == $len) {
$new []= $glue . $char;
$n = 0;
}
}
return $new;
}//mb_chunk_split
/**
* Supporting function for mb_chunk_split
*
* @param string $str
*
* @return array
*
* @see http://php.net/manual/en/function.chunk-split.php
*/
private static function mbStringToArray ($str)
{
if (empty($str)) return false;
$len = mb_strlen($str);
$array = array();
for ($i = 0; $i < $len; $i++) {
$array[] = mb_substr($str, $i, 1);
}
return $array;
}
}//end class
?>

View File

@ -1,81 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Operators_StrictComparisonOperatorSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Operators_StrictComparisonOperatorSniff.
*
* Ensures that only strict comparison operators are used instead of
* equal and not equal operators.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Operators;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class StrictComparisonOperatorSniff implements Sniff
{
private static $_replacements = array(
T_IS_EQUAL => '===',
T_IS_NOT_EQUAL => '!==',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_IS_EQUAL,
T_IS_NOT_EQUAL,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$operator_token = $tokens[$stackPtr];
$operator_string = $operator_token['content'];
$operator_code = $operator_token['code'];
$error_message = '"==" and "!=" are prohibited; use "'
. self::$_replacements[$operator_code] . '" instead of "'
. $operator_string . '".';
$phpcsFile->addError($error_message, $stackPtr, 'NonStrictComparisonUsed');
}//end process()
}//end class
?>

View File

@ -1,465 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Strings;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use Exception;
/**
* CodeIgniter_Sniffs_Strings_DoubleQuoteUsageSniff.
*
* Ensures that double-quoted strings are used only to parse variables,
* to avoid escape characters before single quotes or for chars that need
* to be interpreted like \r, \n or \t.
* If a double-quoted string contain both single and double quotes
* but no variable, then a warning is raised to encourage the use of
* single-quoted strings.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class VariableUsageSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
/*
return array(
T_DOUBLE_QUOTED_STRING,
T_CONSTANT_ENCAPSED_STRING,
);
*/
return array();
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$string = $tokens[$stackPtr]['content'];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$openDblQtStr = substr($string, 0, 1);
if (0 === strcmp($openDblQtStr, '"')) {
$this->processDoubleQuotedString($phpcsFile, $stackPtr, $string);
} else if (0 === strcmp($openDblQtStr, "'")) {
$this->processSingleQuotedString($phpcsFile, $stackPtr, $string);
}
}//end process()
/**
* Processes this test, when the token encountered is a double-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $dblQtString The double-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processDoubleQuotedString (File $phpcsFile, $stackPtr, $dblQtString)
{
$variableFound = FALSE;
$strTokens = token_get_all('<?php '.$dblQtString);
$strPtr = 1; // skip php opening tag added by ourselves
$requireDblQuotes = FALSE;
while ($strPtr < count($strTokens)) {
$strToken = $strTokens[$strPtr];
if (is_array($strToken)) {
if (in_array($strToken[0], array(T_DOLLAR_OPEN_CURLY_BRACES, T_CURLY_OPEN))) {
$strPtr++;
try {
$this->_parseVariable($strTokens, $strPtr);
} catch (Exception $err) {
$error = 'There is no variable, object nor array between curly braces. Please use the escape char for $ or {.';
$phpcsFile->addError($error, $stackPtr, 234);
}
$variableFound = TRUE;
if ('}' !== $strTokens[$strPtr]) {
$error = 'There is no matching closing curly brace.';
$phpcsFile->addError($error, $stackPtr, 345);
}
// don't move forward, since it will be done in the main loop
// $strPtr++;
} else if (T_VARIABLE === $strToken[0]) {
$variableFound = TRUE;
$error = "Variable {$strToken[1]} in double-quoted strings should be enclosed with curly braces. Please consider {{$strToken[1]}}";
$phpcsFile->addError($error, $stackPtr, 456);
}
}
$strPtr++;
}
return $variableFound;
}//end processDoubleQuotedString()
/**
* Processes this test, when the token encountered is a single-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $sglQtString The single-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processSingleQuotedString (File $phpcsFile, $stackPtr, $sglQtString)
{
$variableFound = FALSE;
$strTokens = token_get_all('<?php '.$sglQtString);
$strPtr = 1; // skip php opening tag added by ourselves
while ($strPtr < count($strTokens)) {
$strToken = $strTokens[$strPtr];
if (is_array($strToken)) {
if (T_VARIABLE === $strToken[0]) {
$error = "Variables like {$strToken[1]} should be in double-quoted strings only.";
$phpcsFile->addError($error, $stackPtr);
}
}
$strPtr++;
}
return $variableFound;
}//end processSingleQuotedString()
/**
* Grammar rule to parse the use of a variable. Please notice that it
* doesn't manage the leading $.
*
* _parseVariable ::= <variable>
* | <variable>_parseObjectAttribute()
* | <variable>_parseArrayIndexes()
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return array The attribute name associated to index 'var', an array with
* indexes 'obj' and 'attr' or an array with indexes 'arr' and 'idx'.
*/
private function _parseVariable ($strTokens, &$strPtr)
{
if ( ! in_array($strTokens[$strPtr][0], array(T_VARIABLE, T_STRING_VARNAME))) {
throw new Exception ('Expected variable name.');
}
$var = $strTokens[$strPtr][1];
$strPtr++;
$startStrPtr = $strPtr;
try {
$attr = $this->_parseObjectAttribute($strTokens, $strPtr);
return array ('obj' => $var, 'attr' => $attr);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
try {
$idx = $this->_parseArrayIndexes($strTokens, $strPtr);
return array ('arr' => $var, 'idx' => $idx);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
return array ('var' => $var);
}//end _parseVariable()
/**
* Grammar rule to parse the use of an object attribute.
*
* _parseObjectAttribute ::= -><attribute>
* | -><attribute>_parseObjectAttribute()
* | -><attribute>_parseArrayIndexes()
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return mixed The attribute name as a string, an array with indexes
* 'obj' and 'attr' or an array with indexes 'arr' and 'idx'.
*/
private function _parseObjectAttribute ($strTokens, &$strPtr)
{
if (T_OBJECT_OPERATOR !== $strTokens[$strPtr][0]) {
throw new Exception ('Expected ->.');
}
$strPtr++;
if (T_STRING !== $strTokens[$strPtr][0]) {
throw new Exception ('Expected an object attribute.');
}
$attr = $strTokens[$strPtr][1];
$strPtr++;
$startStrPtr = $strPtr;
try {
$sub_attr = $this->_parseObjectAttribute($strTokens, $strPtr);
return array ('obj' => $attr, 'attr' => $sub_attr);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
try {
$idx = $this->_parseArrayIndexes($strTokens, $strPtr);
return array ('arr' => $attr, 'idx' => $idx);
} catch (Exception $err) {
if ($strPtr !== $startStrPtr) {
throw $err;
}
}
return $attr;
}//end _parseObjectAttribute()
/**
* Grammar rule to parse the use of one or more array indexes.
*
* _parseArrayIndexes ::= _parseArrayIndex()+
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return array Indexes in the same order as in the string.
*/
private function _parseArrayIndexes ($strTokens, &$strPtr)
{
$indexes = array($this->_parseArrayIndex($strTokens, $strPtr));
try {
while (1) {
$startStrPtr = $strPtr;
$indexes [] = $this->_parseArrayIndex($strTokens, $strPtr);
}
} catch (Exception $err) {
if (0 !== ($strPtr - $startStrPtr)) {
throw $err;
}
return $indexes;
}
}//end _parseArrayIndexes()
/**
* Grammar rule to parse the use of array index.
*
* _parseArrayIndex ::= [<index>]
*
* @exception Exception raised if $strTokens starting from $strPtr
* doesn't matched the rule.
*
* @param array $strTokens Tokens to parse.
* @param int $strPtr Pointer to the token where parsing starts.
*
* @return string Index between the 2 square brackets
*/
private function _parseArrayIndex ($strTokens, &$strPtr)
{
if ('[' !== $strTokens[$strPtr]) {
throw new Exception ('Expected [.');
}
$strPtr++;
if (! in_array($strTokens[$strPtr][0], array(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER))) {
throw new Exception ('Expected an array index.');
}
$index = $strTokens[$strPtr][1];
$strPtr++;
if (']' !== $strTokens[$strPtr]) {
throw new Exception ('Expected ].');
}
$strPtr++;
return $index;
}//end _parseArrayIndex()
}//end class
/**
* CodeIgniter_Sniffs_Strings_VariableUsageSniff.
*
* Ensures that variables parsed in double-quoted strings are enclosed with
* braces to prevent greedy token parsing.
* Single-quoted strings don't parse variables, so there is no risk of greedy
* token parsing.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class DoubleQuoteUsageSniff extends VariableUsageSniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_DOUBLE_QUOTED_STRING,
T_CONSTANT_ENCAPSED_STRING,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// no variable are in the string from here
$tokens = $phpcsFile->getTokens();
$qtString = $tokens[$stackPtr]['content'];
// makes sure that it is about a double quote string,
// since variables are not parsed out of double quoted string
$open_qt_str = substr($qtString, 0, 1);
// clean the enclosing quotes
$qtString = substr($qtString, 1, strlen($qtString) - 1 - 1);
if (0 === strcmp($open_qt_str, '"')) {
$this->processDoubleQuotedString($phpcsFile, $stackPtr, $qtString);
} else if (0 === strcmp($open_qt_str, "'")) {
$this->processSingleQuotedString($phpcsFile, $stackPtr, $qtString);
}
}//end process()
/**
* Processes this test, when the token encountered is a double-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $qtString The double-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processDoubleQuotedString (File $phpcsFile, $stackPtr, $qtString)
{
// so there should be at least a single quote or a special char
// if there are the 2 kinds of quote and no special char, then add a warning
$has_variable = parent::processDoubleQuotedString($phpcsFile, $stackPtr, '"'.$qtString.'"');
$has_specific_sequence = $this->_hasSpecificSequence($qtString);
$dbl_qt_at = strpos($qtString, '"');
$smpl_qt_at = strpos($qtString, "'");
if (false === $has_variable && false === $has_specific_sequence
&& false === $smpl_qt_at
) {
$error = 'Single-quoted strings should be used unless it contains variables, special chars like \n or single quotes.';
$phpcsFile->addError($error, $stackPtr, 111);
} else if (false !== $smpl_qt_at && false !== $dbl_qt_at
&& false === $has_variable && false === $has_specific_sequence
) {
$warning = 'It is encouraged to use a single-quoted string, since it doesn\'t contain any variable nor special char though it mixes single and double quotes.';
$phpcsFile->addWarning($warning, $stackPtr, 222);
}
}//end processDoubleQuotedString()
/**
* Processes this test, when the token encountered is a single-quoted string.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $qtString The single-quoted string content,
* i.e. without quotes.
*
* @return void
*/
protected function processSingleQuotedString (File $phpcsFile, $stackPtr, $qtString)
{
// if there is single quotes without additional double quotes,
// then user is allowed to use double quote to avoid having to
// escape single quotes. Don't add the warning, if an error was
// already added, because a variable was found in a single-quoted
// string.
$has_variable = parent::processSingleQuotedString($phpcsFile, $stackPtr, "'".$qtString."'");
$dbl_qt_at = strpos($qtString, '"');
$smpl_qt_at = strpos($qtString, "'");
if (false === $has_variable && false !== $smpl_qt_at && false === $dbl_qt_at) {
$warning = 'You may also use double-quoted strings if the string contains single quotes, so you do not have to use escape characters.';
$phpcsFile->addWarning($warning, $stackPtr, 333);
}
}//end processSingleQuotedString()
/**
* Return TRUE, if a sequence of chars that is parsed in a specific way
* in double-quoted strings is found, FALSE otherwise.
*
* @param string $string String in which sequence of special chars will
* be researched.
*
* @return TRUE, if a sequence of chars that is parsed in a specific way
* in double-quoted strings is found, FALSE otherwise.
*
* @link http://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.double
*/
private function _hasSpecificSequence($string)
{
$hasSpecificSequence = FALSE;
$specialMeaningStrs = array('\n', '\r', '\t', '\v', '\f');
foreach ($specialMeaningStrs as $splStr) {
if (FALSE !== strpos($string, $splStr)) {
$hasSpecificSequence = TRUE;
}
}
$specialMeaningPtrns = array('\[0-7]{1,3}', '\x[0-9A-Fa-f]{1,2}');
foreach ($specialMeaningPtrns as $splPtrn) {
if (1 === preg_match("/{$splPtrn}/", $string)) {
$hasSpecificSequence = TRUE;
}
}
return $hasSpecificSequence;
}//end _hasSpecificSequence()
}//end class
?>

View File

@ -1,87 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowSpaceIndentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas ERNEST
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowSpaceIndentSniff.
*
* Ensures the use of tabs for indentation.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas ERNEST
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class DisallowSpaceIndentSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = array(
'PHP',
'JS',
'CSS',
);
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(T_WHITESPACE);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile All the tokens found in the document.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// Make sure this is whitespace used for indentation.
$line = $tokens[$stackPtr]['line'];
if ($stackPtr > 0 && $tokens[($stackPtr - 1)]['line'] === $line) {
return;
}
if (strpos($tokens[$stackPtr]['content'], " ") !== false) {
$error = 'Tabs must be used to indent lines; spaces are not allowed for code indentation';
$phpcsFile->addError($error, $stackPtr, 'SpacesUsedForIndentation');
}
}//end process()
}//end class
?>

View File

@ -1,95 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowWitheSpaceAroundPhpTagsSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_DisallowWitheSpaceAroundPhpTagsSniff.
*
* Ensures that no whitespace precedes the opening PHP tag
* or follows the closing PHP tag.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class DisallowWitheSpaceAroundPhpTagsSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
T_CLOSE_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$php_tag_token = $tokens[$stackPtr];
$php_tag_code = $php_tag_token['code'];
if (T_OPEN_TAG === $php_tag_code) {
// opening php tag should be the first token.
// any whitespace beofre an opening php tag is tokenized
// as T_INLINE_HTML, so no need to check the content of the token.
$isFirst = 0 === $stackPtr;
if ( ! $isFirst) {
$error = 'Any char before the opening PHP tag is prohibited. Please remove newline or indentation before the opening PHP tag.';
$phpcsFile->addError($error, $stackPtr);
}
} else {
// if (T_CLOSE_TAG === $php_tag_code)
// closing php tag should be the last token
// and it must not contain any whitespace.
$php_tag_string = $php_tag_token['content'];
$isLast = count($tokens) - 1 === $stackPtr;
// both of the two closing php tags contains 2 chars exactly.
$containsEndTagOnly = strlen($php_tag_string) > 2;
if ( ! $isLast || ! $containsEndTagOnly ) {
$error = 'Any char after the closing PHP tag is prohibited. Please removes newline or spaces after the closing PHP tag.';
$phpcsFile->addError($error, $stackPtr);
}
}
}//end process()
}//end class
?>

View File

@ -1,82 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_ElseOnNewLineSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_ElseOnNewLineSniff.
*
* Ensures that control structures else and elseif stand on new lines.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ElseOnNewLineSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_ELSE,
T_ELSEIF,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$else_token = $tokens[$stackPtr];
$previous_non_blank_token_ptr = $phpcsFile->findPrevious(array(T_WHITESPACE), $stackPtr - 1, null, true);
if (false === $previous_non_blank_token_ptr) {
// else is no preceded with any symbol, but it is not the responsibility of this sniff.
return;
}
$previous_non_blank_token = $tokens[$previous_non_blank_token_ptr];
if ($previous_non_blank_token['line'] === $else_token['line']) {
$error = '"' . $else_token['content'] . '" should be on a new line.';
$phpcsFile->addError($error, $stackPtr, 123423);
}
}//end process()
}//end class
?>

View File

@ -1,75 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_WhiteSpace_LogicalNotSpacingSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_WhiteSpace_LogicalNotSpacingSniff.
*
* Ensures that at exactly a space precedes and follows the logical operator !.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\WhiteSpace;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class LogicalNotSpacingSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_BOOLEAN_NOT,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$operator_token = $tokens[$stackPtr];
$previous_token = $tokens[$stackPtr - 1];
$next_token = $tokens[$stackPtr + 1];
if (T_WHITESPACE !== $previous_token['code'] || T_WHITESPACE !== $next_token['code']) {
$error = 'Logical operator ! should always be preceded and followed with a whitespace.';
$phpcsFile->addError($error, $stackPtr, 'badNot');
}
}//end process()
}//end class
?>

View File

@ -1,104 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_AbstractClosingCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_AbstractClosingCommentSniff.
*
* Defines some methods used by
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff
* and CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class AbstractClosingCommentSniff implements Sniff
{
/**
* As an abstract class, this sniff is not associated to any token.
*/
public function register()
{
return array();
}
/**
* As an abstract class, this sniff is not dedicated to process a token.
*/
public function process(File $phpcsFile, $stackPtr)
{
$error = __CLASS__.'::'.__METHOD__.' is abstract. Please develop this method in a child class.';
throw new PHP_CodeSniffer_Exception($error);
}
/**
* Returns the comment without its delimiter(s) as well as leading
* and traling whitespaces.
*
* It removes the first #, the two first / (i.e. //) or the first /*
* and last \*\/. If a comment starts with /**, then the last * will remain
* as well as whitespaces between this star and the comment content.
*
* @param string $comment Comment containing either comment delimiter(s) and
* trailing or leading whitspaces to clean.
*
* @return string Comment without comment delimiter(s) and whitespaces.
*/
protected static function _getCommentContent ($comment)
{
if (self::_stringStartsWith($comment, '#')) {
$comment = substr($comment, 1);
} else if (self::_stringStartsWith($comment, '//')) {
$comment = substr($comment, 2);
} else if (self::_stringStartsWith($comment, '/*')) {
$comment = substr($comment, 2, strlen($comment) - 2 - 2);
}
$comment = trim($comment);
return $comment;
}//_getCommentContent()
/**
* Binary safe string comparison between $needle and
* the beginning of $haystack. Returns true if $haystack starts with
* $needle, false otherwise.
*
* @param string $haystack The string to search in.
* @param string $needle The string to search for.
*
* @return bool true if $haystack starts with $needle, false otherwise.
*/
protected static function _stringStartsWith ($haystack, $needle)
{
$startsWith = false;
if (strlen($needle) <= strlen($haystack)) {
$haystackBeginning = substr($haystack, 0, strlen($needle));
if (0 === strcmp($haystackBeginning, $needle)) {
$startsWith = true;
}
}
return $startsWith;
}//_stringStartsWith()
}//end class
?>

View File

@ -1,109 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ClosingFileCommentSniff.
*
* Ensures that a comment containing the file name is available at the end of file.
* Only other comments and whitespaces are allowed to follow this specific comment.
*
* It may be all kind of comment like multi-line and inline C-style comments as
* well as PERL-style comments. Any number of white may separate comment delimiters
* from comment content. However, content has to be equal to template
* "End of file <file_name>". Comparison between content and template is
* case-sensitive.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
class ClosingFileCommentSniff extends AbstractClosingCommentSniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$fullFilename = $phpcsFile->getFilename();
$filename = basename($fullFilename);
$commentTemplate = "End of file $filename";
$tokens = $phpcsFile->getTokens();
$currentToken = count($tokens) - 1;
$hasClosingFileComment = false;
$isNotAWhitespaceOrAComment = false;
while ($currentToken >= 0
&& ! $isNotAWhitespaceOrAComment
&& ! $hasClosingFileComment
) {
$token = $tokens[$currentToken];
$tokenCode = $token['code'];
if (T_COMMENT === $tokenCode) {
$commentString = self::_getCommentContent($token['content']);
if (0 === strcmp($commentString, $commentTemplate)) {
$hasClosingFileComment = true;
}
} else if (T_WHITESPACE === $tokenCode) {
// Whitespaces are allowed between the closing file comment,
// other comments and end of file
} else {
$isNotAWhitespaceOrAComment = true;
}
$currentToken--;
}
if ( ! $hasClosingFileComment) {
$error = 'No comment block marks the end of file instead of the closing PHP tag. Please add a comment block containing only "' . $commentTemplate . '".';
$phpcsFile->addError($error, $currentToken);
}
}//end process()
}//end class
?>

View File

@ -1,182 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff.
*
* Ensures that a comment containing the file location exists at the end of file.
* Only other comments and whitespaces are allowed between this comment and
* the end of file.
*
* It may be all kind of comment like multi-line and inline C-style comments as
* well as PERL-style comments. Any number of white may separate comment delimiters
* from comment content. However, content has to be equal to template
* "Location: <file_path_relative_to_application_root>".
* Comparison between content and template is case-sensitive.
*
* There are several ways to configure the application root. In order of priority :
* - Configuration variable ci_application_root.
* - Rule property applicationRoot.
* - Default value '/application/'
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2006 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Common;
class ClosingLocationCommentSniff extends AbstractClosingCommentSniff
{
public $applicationRoot = '/application/';
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_OPEN_TAG
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// We are only interested if this is the first open tag.
if ($stackPtr !== 0) {
if ($phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)) !== false) {
return;
}
}
$filePath = $phpcsFile->getFilename();
$tokens = $phpcsFile->getTokens();
// removes the application root from the beginning of the file path
$locationPath = self::_getLocationPath($filePath, $this->_getAppRoot());
// add an error, if application root doesn't exist in current file path
if (false === $locationPath) {
$error = 'Unable to find "' . $this->_getAppRoot() . '" in file path "' . $filePath . '". Please set your project\'s application root.';
$phpcsFile->addError($error, count($tokens) - 1);
return;
}
// generates the expected comment
$commentTemplate = "Location: $locationPath";
$currentToken = count($tokens) - 1;
$hasClosingLocationComment = false;
$isNotAWhitespaceOrAComment = false;
while ($currentToken >= 0
&& ! $isNotAWhitespaceOrAComment
&& ! $hasClosingLocationComment
) {
$token = $tokens[$currentToken];
$tokenCode = $token['code'];
if (T_COMMENT === $tokenCode) {
$commentString = self::_getCommentContent($token['content']);
if (0 === strcmp($commentString, $commentTemplate)) {
$hasClosingLocationComment = true;
}
} else if (T_WHITESPACE === $tokenCode) {
// Whitespaces are allowed between the closing file comment,
//other comments and end of file
} else {
$isNotAWhitespaceOrAComment = true;
}
$currentToken--;
}
if ( ! $hasClosingLocationComment) {
$error = 'No comment block marks the end of file instead of the closing PHP tag. Please add a comment block containing only "' . $commentTemplate . '".';
$phpcsFile->addError($error, $currentToken);
}
}//end process()
/**
* Returns the relative path from $appRoot to $filePath, or false if
* $appRoot cannot be found in $filePath, because $appRoot is not a parent
* of $filePath.
*
* @param string $filePath Full path to the file being proceed.
* @param string $appRoot Partial or full path to the CodeIgniter
* application root of the file being proceed. It must not contain the
* full path to the application root, but at least the name of the
* application root. Parent directory of the application root are allowed
* but not mandatory.
*
* @return string|bool The relative path from $appRoot to $filePath, or
* false if $appRoot cannot be found in $filePath.
*/
private static function _getLocationPath ($filePath, $appRoot)
{
// removes the path to application root
// from the beginning of the file path
$appRootAt = strpos($filePath, $appRoot);
if (false === $appRootAt) {
return false;
}
$localPath = substr($filePath, $appRootAt + strlen($appRoot));
// ensures the location path to be a relative path starting with "./".
if ( ! self::_stringStartsWith($localPath, './')) {
$localPath = './' . $localPath;
} else if ( ! self::_stringStartsWith($localPath, '.')
&& self::_stringStartsWith($localPath, '/')
) {
$localPath = '.' . $localPath;
}
return $localPath;
}//end _getLocationPath()
/**
* Returns the application root that should be used first.
*
* There are several ways to configure the application root.
* In order of priority :
* - Configuration variable ci_application_root.
* - Rule property applicationRoot.
* - Default value '/application/'
*
* @return string Path to your project application root.
*/
private function _getAppRoot()
{
$appRoot = Common::getConfigData('ci_application_root');
if (null === $appRoot) {
$appRoot = $this->applicationRoot;
}
return $appRoot;
}//end _getAppRoot()
}//end class
?>

View File

@ -1,142 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ConstructorNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
use PHP_CodeSniffer\Files\File;
/**
* CodeIgniter_Sniffs_NamingConventions_ConstructorNameSniff.
*
* Favor PHP 4 constructor syntax, which uses "function ClassName()".
* Avoid PHP 5 constructor syntax, which uses "function __construct()".
*
* @todo Try to avoid overly long and verbose names.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@gmail.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
class ConstructorNameSniff extends AbstractScopeSniff
{
public $php5Constructors = '1';
/**
* Constructs the test with the tokens it wishes to listen for.
*
* @return void
*/
public function __construct()
{
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}//end __construct()
/**
* Processes this test when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $currScope A pointer to the start of the scope.
*
* @return void
*/
protected function processTokenWithinScope(
File $phpcsFile,
$stackPtr,
$currScope
) {
$methodName = $phpcsFile->getDeclarationName($stackPtr);
$className = $phpcsFile->getDeclarationName($currScope);
$isPhp4Constructor = strcasecmp($methodName, $className) === 0;
$isPhp5Constructor = strcasecmp($methodName, '__construct') === 0;
if ($this->php5Constructors != '0') {
if ($isPhp4Constructor) {
$error = "PHP4 style constructors are not allowed; use \"__construct\" instead";
$phpcsFile->addError($error, $stackPtr);
}
} else {
if ($isPhp5Constructor) {
$error = "PHP5 style constructors are not allowed; use \"$className\" instead";
$phpcsFile->addError($error, $stackPtr);
}
}
if ( ! $isPhp4Constructor && ! $isPhp5Constructor ) {
return;
}
$tokens = $phpcsFile->getTokens();
$parentClassName = $phpcsFile->findExtendedClassName($currScope);
$wrongConstructor = '';
// prepares the error message and wrong constructor
if ($this->php5Constructors != '0') {
$error = 'PHP4 style calls to parent constructors are not allowed.';
$error = "$error Please use \"parent::__construct\" instead.";
if (false !== $parentClassName) {
$wrongConstructor = $parentClassName;
}
// Else $wrongConstructor will be empty
// and the test expression will always be false.
// It doesn't check that no parent method should be called
// when no parent class is defined.
} else {
$error = 'PHP5 style calls to parent constructors are not allowed.';
if (false !== $parentClassName) {
$error = "$error Please use \"parent::$parentClassName\" instead.";
}
$wrongConstructor = '__construct';
}
// looks for the use of a wrong constructor.
$endFunctionIndex = $tokens[$stackPtr]['scope_closer'];
$doubleColonIndex = $phpcsFile->findNext(
array(T_DOUBLE_COLON),
$stackPtr,
$endFunctionIndex
);
while ($doubleColonIndex) {
if ($tokens[($doubleColonIndex + 1)]['code'] === T_STRING
&& $tokens[($doubleColonIndex + 1)]['content'] === $wrongConstructor
) {
$phpcsFile->addError($error, ($doubleColonIndex + 1));
}
$doubleColonIndex = $phpcsFile->findNext(
array(T_DOUBLE_COLON),
$doubleColonIndex + 1,
$endFunctionIndex
);
}
}//end processTokenWithinScope()
protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
{
// TODO: Implement processTokenOutsideScope() method.
}
}//end class
?>

View File

@ -1,84 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidClassNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidClassNameSniff.
*
* Ensures that class and interface names have their first letter uppercase
* and that words are separated with an underscore, and not CamelCased.
*
* @todo Try to avoid overly long and verbose names in using property rule and
* configuration variable to set limits. Have a look at
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ValidClassNameSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
// get the class name
$className = trim($phpcsFile->getDeclarationName($stackPtr));
// compute the expected class name
// [^_] means "something different from _", but not "nothing or something different from _"
$lcClassNameChunk = preg_replace('/([^_])([A-Z])/', '${1}_${2}', $className);
$expectedClassName
= strtoupper($className[0]) . strtolower(substr($lcClassNameChunk,1));
// ensures that the current class name
// and the expected class name are identical
if (0 !== strcmp($className, $expectedClassName)) {
$error = 'Class names should always have their first letter uppercase. Multiple words should be separated with an underscore, and not CamelCased. Please consider ' . $expectedClassName . ' instead of ' . $className . '.';
$phpcsFile->addError($error, $stackPtr);
}
}//end process()
}//end class
?>

View File

@ -1,84 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidFileNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidFileNameSniff.
*
* Tests that the file name matchs the name of the class that it contains in lower case.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baobaz.com>
* @copyright 2011 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class ValidFileNameSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array(
T_CLASS,
T_INTERFACE,
);
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
// computes the expected filename based on the name of the class or interface that it contains.
$decNamePtr = $phpcsFile->findNext(T_STRING, $stackPtr);
$decName = $tokens[$decNamePtr]['content'];
$expectedFileName = strtolower($decName);
// extracts filename without extension from its path.
$fullPath = $phpcsFile->getFilename();
$fileNameAndExt = basename($fullPath);
$fileName = substr($fileNameAndExt, 0, strrpos($fileNameAndExt, '.'));
if ($expectedFileName !== $fileName) {
$errorTemplate = 'Filename "%s" doesn\'t match the name of the %s that it contains "%s" in lower case. "%s" was expected.';
$errorMessage = sprintf(
$errorTemplate,
$fileName,
strtolower($tokens[$stackPtr]['content']), // class or interface
$decName,
$expectedFileName
);
$phpcsFile->addError($errorMessage, 0);
}
}//end process()
}//end class
?>

View File

@ -1,161 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
*
* Ensures that class methods and functions areentirely lowercased and that
* words are separated with an underscore, and not CamelCased.
* Ensures that private class methods are prefixed with an underscore and that
* all other methods are not prefixed with an underscored.
* Ensures that names longer than 50 chars are prohibited. Likewise names longer
* than 35 chars raise a warning.
*
* @todo Use a rule property or a configuration variable to allow users to set
* their own maximum lengths for function and method names. Have a look at
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff and application root.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstactScopeSniff;
use PHP_CodeSniffer\Files\File;
class ValidMethodNameSniff extends AbstractScopeSniff
{
/**
* A list of all PHP magic methods.
*
* @var array
*/
protected static $magicMethods = array(
'construct',
'destruct',
'call',
'callStatic',
'get',
'set',
'isset',
'unset',
'sleep',
'wakeup',
'toString',
'set_state',
'clone',
);
/**
* Defines which token(s) in which scope(s) will be proceed.
*/
public function __construct()
{
parent::__construct(array(T_CLASS, T_INTERFACE), array(T_FUNCTION), true);
}//end __construct()
/**
* Processes the tokens within the scope.
*
* @param File $phpcsFile The file being processed.
* @param int $stackPtr The position where this token was
* found.
* @param int $currScope The position of the current scope.
*
* @return void
*/
protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
{
$methodName = $phpcsFile->getDeclarationName($stackPtr);
if ($methodName === null) {
// Ignore closures.
return;
}
$className = $phpcsFile->getDeclarationName($currScope);
// Is this a magic method i.e. is prefixed with "__".
if (0 === strcmp(substr($methodName, 0, 2), '__')) {
$magicPart = substr($methodName, 2);
if (in_array($magicPart, self::$magicMethods) === false) {
$error = "Method name \"$className::$methodName\" is invalid; only PHP magic methods should be prefixed with a double underscore";
$phpcsFile->addError($error, $stackPtr);
}
return;
}
// PHP4 constructors are allowed to break our rules.
if ($methodName === $className) {
return;
}
// PHP4 destructors are allowed to break our rules.
if ($methodName === '_'.$className) {
return;
}
if (0 !== strcmp($methodName, strtolower($methodName))) {
$uscrdMethodName = preg_replace('/([A-Z])/', '_${1}', $methodName);
$expectedMethodName = strtolower($uscrdMethodName);
$error = "Class methods should be entirely lowercased. Please consider \"$expectedMethodName\" instead of \"$methodName\".";
$phpcsFile->addError($error, $stackPtr);
return;
}
$methodProps = $phpcsFile->getMethodProperties($stackPtr);
$scope = $methodProps['scope'];
$scopeSpecified = $methodProps['scope_specified'];
// If it's a private method, it must have an underscore on the front.
if ($scope === 'private' && $methodName{0} !== '_') {
$error = "Private method name \"$className::$methodName\" must be prefixed with an underscore";
$phpcsFile->addError($error, $stackPtr);
return;
}
// If it's not a private method, it must not have an underscore on the front.
if ($scope !== 'private' && $methodName{0} === '_') {
if (true === $scopeSpecified) {
$error = "Public method name \"$className::$methodName\" must not be prefixed with an underscore";
} else {
$error = ucfirst($scope)." method name \"$className::$methodName\" must not be prefixed with an underscore";
}
$phpcsFile->addError($error, $stackPtr);
return;
}
// If name is too verbose,
// then either an error or a warning is displayed.
$error_limit = 50;
$warning_limit = 35;
if (strlen($methodName) > $error_limit) {
$error = "Overly long and verbose names are prohibited. Please find a name shorter than $error_limit chars.";
$phpcsFile->addError($error, $stackPtr);
return;
} else if (strlen($methodName) > $warning_limit) {
$warning = "Try to avoid overly long and verbose names in finding a name shorter than $warning_limit chars.";
$phpcsFile->addWarning($warning, $stackPtr);
}
}//end processTokenWithinScope()
}//end class
?>

View File

@ -1,562 +0,0 @@
<?php
/**
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* PHP version 5
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
/**
* CodeIgniter_Sniffs_NamingConventions_ValidVariableNameSniff.
*
* Ensures that variable names contain only lowercase letters,
* use underscore separators.
* Ensures that class attribute names are prefixed with an underscore,
* only when they are private.
* Ensure that variable names are longer than 3 chars except those declared
* in for loops.
*
* @todo Try to avoid overly long and verbose names in using property rule and
* configuration variable to set limits. Have a look at
* CodeIgniter_Sniffs_NamingConventions_ValidMethodNameSniff.
* @todo Use a property rule or a configuration variable to allow users to set
* minimum variable name length. Have a look at
* CodeIgniter_Sniffs_Files_ClosingLocationCommentSniff and application root.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Thomas Ernest <thomas.ernest@baoabz.com>
* @copyright 2010 Thomas Ernest
* @license http://thomas.ernest.fr/developement/php_cs/licence GNU General Public License
* @link http://pear.php.net/package/PHP_CodeSniffer
*/
namespace CodeIgniter\Sniffs\NamingConventions;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
use PHP_CodeSniffer\Files\File;
class ValidVariableNameSniff extends AbstractVariableSniff
{
/**
* Processes class member variables.
*
* @param File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
// get variable name and properties
$tokens = $phpcsFile->getTokens();
$varTk = $tokens[$stackPtr];
$varName = substr($varTk['content'], 1);
$varProps = $phpcsFile->getMemberProperties($stackPtr);
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName) ) {
return;
}
if ( ! $this->checkVisibilityPrefix($phpcsFile, $stackPtr, $varName, $varProps)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
}//end processMemberVar()
/**
* Processes normal variables.
*
* @param File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
// get variable name
$tokens = $phpcsFile->getTokens();
$varTk = $tokens[$stackPtr];
$varName = substr($varTk['content'], 1);
// skip the current object variable, i.e. $this
if (0 === strcmp($varName, 'this')) {
return;
}
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
}//end processVariable()
/**
* Processes variables in double quoted strings.
*
* @param File $phpcsFile The file where this token was found.
* @param int $stackPtr The position where the token was found.
*
* @return void
*/
protected function processVariableInString(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$stringTk = $tokens[$stackPtr];
$stringString = $stringTk['content'];
$varAt = self::_getVariablePosition($stringString, 0);
while (false !== $varAt) {
// get variable name
$matches = array();
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', substr($stringString, $varAt), $matches);
$varName = $matches[1];
// check(s)
if ( ! $this->checkLowerCase($phpcsFile, $stackPtr, $varName)) {
return;
}
if ( ! $this->checkLength($phpcsFile, $stackPtr, $varName)) {
return;
}
// prepare checking next variable
$varAt = self::_getVariablePosition($stringString, $varAt + 1);
}
}//end processVariableInString()
/**
* Checks that the variable name is all in lower case, else it add an error
* to $phpcsFile. Returns true if variable name is all in lower case, false
* otherwise.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return bool true if variable name is all in lower case, false otherwise.
*/
protected function checkLowerCase(File $phpcsFile, $stackPtr, $varName)
{
$isInLowerCase = true;
if (0 !== strcmp($varName, strtolower($varName))) {
// get the expected variable name
$varNameWithUnderscores = preg_replace('/([A-Z])/', '_${1}', $varName);
$expectedVarName = strtolower(ltrim($varNameWithUnderscores, '_'));
// adapts the error message to the error case
if (strlen($varNameWithUnderscores) > strlen($varName)) {
$error = 'Variables should not use CamelCasing or start with a Capital.';
} else {
$error = 'Variables should be entirely lowercased.';
}
$error = $error . 'Please consider "' . $expectedVarName
. '" instead of "' . $varName . '".';
// adds the error and changes return value
$phpcsFile->addError($error, $stackPtr);
$isInLowerCase = false;
}
return $isInLowerCase;
}//end checkLowerCase()
/**
* Checks that an underscore is used at the beginning of a variable only if
* it is about a private variable. If it isn't a private variable, then it
* must not be prefixed with an underscore. Returns true if $varName is
* properly prefixed according to the variable visibility provided in
* $varProps, false otherwise.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
* @param array $varProps Member variable properties like
* its visibility.
*
* @return bool true if variable name is prefixed with an underscore only
* when it is about a private variable, false otherwise.
*/
protected function checkVisibilityPrefix(File $phpcsFile, $stackPtr, $varName, $varProps)
{
$isVisibilityPrefixRight = true;
$scope = $varProps['scope'];
// If it's a private variable, it must have an underscore on the front.
if ($scope === 'private' && $varName{0} !== '_') {
$error = "Private variable name \"$varName\" must be prefixed with an underscore";
$phpcsFile->addError($error, $stackPtr);
$isVisibilityPrefixRight = false;
} else if ($scope !== 'private' && $varName{0} === '_') {
// If it's not a private variable,
// then it must not start with an underscore.
if (isset ($scopeSpecified) && true === $scopeSpecified) {
$error = "Public variable name \"$varName\" must not be prefixed with an underscore";
} else {
$error = ucfirst($scope) . " variable name \"$varName\" must not be prefixed with an underscore";
}
$phpcsFile->addError($error, $stackPtr);
$isVisibilityPrefixRight = false;
}
return $isVisibilityPrefixRight;
}//end checkVisibilityPrefix()
/**
* Checks that variable name length is not too short. Returns true, if it
* meets minimum length requirement, false otherwise.
*
* A variable name is too short if it is shorter than the minimal
* length and it isn't in the list of allowed short names nor declared in a
* for loop (in which it would be nested).
* The minimal length is defined in the function. It is 3 chars now.
* The list of allowed short names is defined in the function.
* It is case-sensitive. It contains only 'ci' now.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return bool false if variable name $varName is shorter than the minimal
* length and it isn't in the list of allowed short names nor declared in a
* for loop (in which it would be nested), otherwise true.
*/
protected function checkLength(File $phpcsFile, $stackPtr, $varName)
{
$minLength = 3;
$allowedShortName = array('ci');
$isLengthRight = true;
// cleans variable name
$varName = ltrim($varName, '_');
if (strlen($varName) <= $minLength) {
// skips adding an error, if it is a specific variable name
if (in_array($varName, $allowedShortName)) {
return $isLengthRight;
}
// skips adding an error, if the variable is in a for loop
if (false !== self::_isInForLoop($phpcsFile, $stackPtr, $varName)) {
return $isLengthRight;
}
// adds the error message finally
$error = 'Very short'
. (
$minLength > 0 ?
' (i.e. less than ' . ($minLength + 1) . ' chars)'
: ''
)
. ', non-word variables like "' . $varName
. '" should only be used as iterators in for() loops.';
$phpcsFile->addError($error, $stackPtr);
$isLengthRight = false;
}
return $isLengthRight;
}//end checkLength()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in a for loop, otherwise false.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the head of a for loop, otherwise false.
*/
private static function _isInForLoop(File $phpcsFile, $stackPtr, $varName)
{
$keepLookingFromPtr = $stackPtr;
while (false !== $keepLookingFromPtr) {
// looks if it is in (head or body) of a for loop
$forPtr = self::_isInForLoopHead($phpcsFile, $keepLookingFromPtr);
if (false === $forPtr) {
$forPtr = self::_isInForLoopBody($phpcsFile, $keepLookingFromPtr);
}
// checks if it is declared in here and prepares next step
if (false !== $forPtr) {
if (false !== self::_isDeclaredInForLoop($phpcsFile, $forPtr, $varName)) {
return $forPtr;
}
$keepLookingFromPtr = $forPtr;
} else {
$keepLookingFromPtr = false;
}
}
return false;
}//end _isInForLoop()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in the head of a for loop, otherwise false.
* The head is the code placed between parenthesis next to the key word
* 'for' : for (<loop_head>) {<loop_body>}.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the head of a for loop, otherwise false.
*/
private static function _isInForLoopHead(File $phpcsFile, $stackPtr)
{
$isInForLoop = false;
$tokens = $phpcsFile->getTokens();
$currentTk = $tokens[$stackPtr];
if (array_key_exists('nested_parenthesis', $currentTk)) {
$nestedParenthesis = $currentTk['nested_parenthesis'];
foreach ( $nestedParenthesis as $openParPtr => $closeParPtr) {
$nonWhitspacePtr = $phpcsFile->findPrevious(
array(T_WHITESPACE),
$openParPtr - 1,
null,
true,
null,
true
);
if (false !== $nonWhitspacePtr) {
$isFor = T_FOR === $tokens[$nonWhitspacePtr]['code'];
if ($isFor) {
$isInForLoop = $nonWhitspacePtr;
break;
}
}
}
}
return $isInForLoop;
}//end _isInForLoopHead()
/**
* Returns the position of closest previous T_FOR, if token associated with
* $stackPtr in $phpcsFile is in the body of a for loop, otherwise false.
* The body are the instructions placed after parenthesis of a 'for'
* declaration, enclosed with curly brackets usually.
* 'for' : for (<loop_head>) {<loop_body>}.
*
* @param File $phpcsFile The current file being processed.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return int|bool Position of T_FOR if token associated with $stackPtr in
* $phpcsFile is in the body of a for loop, otherwise false.
*/
private static function _isInForLoopBody(File $phpcsFile, $stackPtr)
{
$isInForLoop = false;
$tokens = $phpcsFile->getTokens();
// get englobing hierarchy
$parentPtrAndCode = $tokens[$stackPtr]['conditions'];
krsort($parentPtrAndCode);
// looks for a for loop having a body not enclosed with curly brackets,
// which involves that its body contains only one instruction.
if (is_array($parentPtrAndCode) && ! empty($parentPtrAndCode)) {
$parentCode = reset($parentPtrAndCode);
$parentPtr = key($parentPtrAndCode);
$openBracketPtr = $tokens[$parentPtr]['scope_opener'];
} else {
$parentCode = 0;
$parentPtr = 0;
$openBracketPtr = 0;
}
$openResearchScopePtr = $stackPtr;
// recursive search, since a for statement may englobe other inline
// control statement or may be near to function calls, etc...
while (false !== $openResearchScopePtr) {
$closeParPtr = $phpcsFile->findPrevious(
array(T_CLOSE_PARENTHESIS),
$openResearchScopePtr,
null,
false,
null,
true
);
// is there a closing parenthesis with a control statement before
// the previous instruction ?
if (false !== $closeParPtr) {
// is there no opening curly bracket specific to
// set of instructions, between the closing parenthesis
// and the current token ?
if ($openBracketPtr < $closeParPtr) {
// starts the search from the token before the closing
// parenthesis, if it isn't a for statement
$openResearchScopePtr = $closeParPtr - 1;
// is this parenthesis associated with a for statement ?
$closeParenthesisTk = $tokens[$closeParPtr];
if (array_key_exists('parenthesis_owner', $closeParenthesisTk)) {
$mayBeForPtr = $closeParenthesisTk['parenthesis_owner'];
$mayBeForTk = $tokens[$mayBeForPtr];
if (T_FOR === $mayBeForTk['code']) {
return $mayBeForPtr;
}
}
} else {
// if it is about a for loop, don't go further
// and detect it after one more loop execution, do it now
if (T_FOR === $parentCode) {
return $parentPtr;
}
// starts the search from the token before the one
// englobing the current statement
$openResearchScopePtr = $parentPtr - 1;
// re-initialize variables about the englobing structure
if (is_array($parentPtrAndCode)) {
$parentCode = next($parentPtrAndCode);
$parentPtr = key($parentPtrAndCode);
$openBracketPtr = $tokens[$parentPtr]['scope_opener'];
}
}
} else {
$openResearchScopePtr = false;
}
}
// looks for a for loop having a body enclosed with curly brackets
foreach ($parentPtrAndCode as $parentPtr => $parentCode) {
if (T_FOR === $parentCode) {
return $parentPtr;
}
}
return false;
}//end _isInForLoopBody()
/**
* Returns true if a variable declared in the head of the for loop pointed
* by $forPtr in file $phpcsFile has the name $varName.
*
* @param File $phpcsFile The current file being processed.
* @param int $forPtr The position of the 'for' token
* in the stack passed in $tokens.
* @param string $varName The name of the variable to
* procced without $, { nor }.
*
* @return int|bool true if a variable declared in the head of the for loop
* pointed by $forPtr in file $phpcsFile has the name $varName.
*/
private static function _isDeclaredInForLoop(File $phpcsFile, $forPtr, $varName)
{
$isDeclaredInFor = false;
$tokens = $phpcsFile->getTokens();
$forVarPtrs = self::_getVarDeclaredInFor($phpcsFile, $forPtr);
foreach ($forVarPtrs as $forVarPtr) {
$forVarTk = $tokens[$forVarPtr];
// get variable name
$matches = array();
preg_match('/^\$\{?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}?/', $forVarTk['content'], $matches);
$forVarName = $matches[1];
if (0 === strcmp($forVarName, $varName)) {
$isDeclaredInFor = $forVarPtr;
break;
}
}
return $isDeclaredInFor;
}//end _isDeclaredInForLoop()
/**
* Returns list of pointers to variables declared in for loop associated to
* $forPtr in file $phpcsFile.
*
* All pointers in the result list are pointing to token with code
* T_VARIABLE. An exception is raised, if $forPtr doesn't point a token with
* code T_FOR.
*
* @param File $phpcsFile The current file being processed.
* @param int $forPtr The position of the current token
* in the stack passed in $tokens.
*
* @return array List of pointers to variables declared in for loop $forPtr.
*/
private static function _getVarDeclaredInFor(File $phpcsFile, $forPtr)
{
$tokens = $phpcsFile->getTokens();
$forTk = $tokens[$forPtr];
if (T_FOR !== $forTk['code']) {
throw new PHP_CodeSniffer_Exception('$forPtr must be of type T_FOR');
}
$openParPtr = $forTk['parenthesis_opener'];
$openParenthesisTk = $tokens[$openParPtr];
$endOfDeclPtr = $phpcsFile->findNext(array(T_SEMICOLON), $openParPtr);
$forVarPtrs = array();
$varPtr = $phpcsFile->findNext(
array(T_VARIABLE),
$openParPtr + 1,
$endOfDeclPtr
);
while (false !== $varPtr) {
$forVarPtrs [] = $varPtr;
$varPtr = $phpcsFile->findNext(
array(T_VARIABLE),
$varPtr + 1,
$endOfDeclPtr
);
}
return $forVarPtrs;
}//end _getVarDeclaredInFor()
/**
* Returns the position of first occurrence of a PHP variable starting with
* $ in $haystack from $offset.
*
* @param string $haystack The string to search in.
* @param int $offset The optional offset parameter allows you to
* specify which character in haystack to start
* searching. The returned position is still
* relative to the beginning of haystack.
*
* @return mixed The position as an integer
* or the boolean false, if no variable is found.
*/
private static function _getVariablePosition($haystack, $offset = 0)
{
$var_starts_at = strpos($haystack, '$', $offset);
$is_a_var = false;
while (false !== $var_starts_at && ! $is_a_var) {
// makes sure that $ is used for a variable and not as a symbol,
// if $ is protected with the escape char, then it is a symbol.
if (0 !== strcmp($haystack[$var_starts_at - 1], '\\')) {
if (0 === strcmp($haystack[$var_starts_at + 1], '{')) {
// there is an opening brace in the right place
// so it looks for the closing brace in the right place
$hsChunk2 = substr($haystack, $var_starts_at + 2);
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\}/', $hsChunk2)) {
$is_a_var = true;
}
} else {
$hsChunk1 = substr($haystack, $var_starts_at + 1);
if (1 === preg_match('/^[a-zA-Z_\x7f-\xff]/', $hsChunk1)) {
// $ is used for a variable and not as a symbol,
// since what follows $ matchs the definition of
// a variable label for PHP.
$is_a_var = true;
}
}
}
// update $var_starts_at for the next variable
// only if no variable was found, since it is returned otherwise.
if ( ! $is_a_var) {
$var_starts_at = strpos($haystack, '$', $var_starts_at + 1);
}
}
if ($is_a_var) {
return $var_starts_at;
} else {
return false;
}
}//end _getVariablePosition()
}//end class
?>

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ruleset name="CodeIgniter">
<description>CodeIgniter coding standard as described at http://codeigniter.com/user_guide/general/styleguide.html.</description>
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<!-- PHP files should OMIT the closing PHP tag -->
<rule ref="Zend.Files.ClosingTag"/>
<!-- Always use full PHP opening tags -->
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<!-- Constants should always be fully uppercase -->
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<!-- TRUE, FALSE, and NULL keywords should always be fully uppercase -->
<rule ref="Generic.PHP.UpperCaseConstant"/>
<!-- One statement per line -->
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<!-- Classes and functions should be commented -->
<rule ref="PEAR.Commenting.ClassComment"/>
<rule ref="PEAR.Commenting.FunctionComment"/>
<!-- <rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>-->
<!-- Use warnings for docblock comments for files and variables, since nothing is cleary explained -->
<rule ref="PEAR.Commenting.FileComment">
<properties>
<property name="error" value="false"/>
</properties>
</rule>
<rule ref="Squiz.Commenting.VariableComment">
<properties>
<property name="error" value="false"/>
</properties>
</rule>
<!-- Use Allman style indenting. With the exception of Class declarations,
braces are always placed on a line by themselves, and indented at the same level as the control statement that "owns" them. -->
<rule ref="Generic.Functions.OpeningFunctionBraceBsdAllman"/>
<rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<!-- @todo Please see PHP_CodeSniffer_Standards_CodeIgniter_CodeIgniterCodingStandard for more details about what there is to do -->
</ruleset>

View File

@ -1,29 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
stopOnFailure="false"
bootstrap="../tests/bootstrap.php"
beStrictAboutTestsThatDoNotTestAnything="true"
>
<filter>
<whitelist>
<directory suffix=".php">../src</directory>
</whitelist>
</filter>
<testsuites>
<testsuite name="AnimeClient">
<directory>../tests</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-html" target="../coverage"/>
<log type="coverage-clover" target="logs/clover.xml"/>
</logging>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0" />
<server name="HTTP_HOST" value="localhost" />
<server name="SERVER_NAME" value="localhost" />
<server name="REQUEST_URI" value="/" />
<server name="REQUEST_METHOD" value="GET" />
</php>
</phpunit>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" stopOnFailure="false" bootstrap="../tests/bootstrap.php" beStrictAboutTestsThatDoNotTestAnything="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd">
<coverage>
<report>
<clover outputFile="logs/clover.xml"/>
<html outputDirectory="../coverage"/>
</report>
</coverage>
<testsuites>
<testsuite name="AnimeClient">
<directory>../tests/AnimeClient</directory>
</testsuite>
<testsuite name="Ion">
<directory>../tests/Ion</directory>
</testsuite>
</testsuites>
<logging>
<junit outputFile="logs/junit.xml"/>
</logging>
<php>
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0"/>
<server name="HTTP_HOST" value="localhost"/>
<server name="SERVER_NAME" value="localhost"/>
<server name="REQUEST_URI" value="/"/>
<server name="REQUEST_METHOD" value="GET"/>
</php>
<source>
<include>
<directory suffix=".php">../src</directory>
</include>
</source>
</phpunit>

View File

@ -1,94 +0,0 @@
<?php
declare(strict_types=1);
$file_patterns = [
'app/bootstrap.php',
'migrations/*.php',
'src/**/*.php',
'src/*.php',
'tests/**/*.php',
'tests/*.php',
'index.php',
'Robofile.php'
];
if ( ! function_exists('glob_recursive'))
{
// Does not support flag GLOB_BRACE
function glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir)
{
$files = array_merge($files, glob_recursive($dir . '/' . basename($pattern), $flags));
}
return $files;
}
}
function get_text_to_replace(array $tokens): string
{
$output = '';
// Tokens have the follow structure if arrays:
// [0] => token type constant
// [1] => raw syntax parsed to that token
// [2] => line number
foreach($tokens as $token)
{
// Since we only care about opening docblocks,
// bail out when we get to the namespace token
if (is_array($token) && $token[0] === T_NAMESPACE)
{
break;
}
if (is_array($token))
{
$token = $token[1];
}
$output .= $token;
}
return $output;
}
function get_tokens($source): array
{
return token_get_all($source);
}
function replace_files(array $files, $template)
{
print_r($files);
foreach ($files as $file)
{
$source = file_get_contents($file);
if (stripos($source, 'namespace') === FALSE)
{
continue;
}
$tokens = get_tokens($source);
$text_to_replace = get_text_to_replace($tokens);
$header = file_get_contents(__DIR__ . $template);
$new_text = "<?php declare(strict_types=1);\n{$header}";
$new_source = str_replace($text_to_replace, $new_text, $source);
file_put_contents($file, $new_source);
}
}
foreach ($file_patterns as $glob)
{
$files = glob_recursive($glob);
replace_files($files, '/header_comment.txt');
}
echo "Successfully updated headers \n";

View File

@ -13,8 +13,8 @@
"autoload": {
"files": [
"src/Ion/functions.php",
"src/AnimeClient/constants.php",
"src/AnimeClient/AnimeClient.php"
"src/AnimeClient.php",
"src/AnimeClient/constants.php"
],
"psr-4": {
"Aviat\\": "src/"
@ -23,61 +23,46 @@
"autoload-dev": {
"psr-4": {
"Aviat\\AnimeClient\\Tests\\": "tests/AnimeClient",
"Aviat\\Ion\\Tests\\": "tests/Ion",
"CodeIgniter\\": "build/CodeIgniter/"
"Aviat\\Ion\\Tests\\": "tests/Ion"
}
},
"config": {
"lock": false,
"platform": {
"php": "7.3"
}
"lock": false
},
"require": {
"amphp/http-client": "^4.2",
"aura/html": "^2.0",
"aura/router": "^3.0",
"aura/session": "^2.0",
"aviat/banker": "^2.0.0",
"aviat/query": "^2.5.1",
"danielstjules/stringy": "^3.1.0",
"amphp/http-client": "^v5.0.0",
"aura/html": "^2.5.0",
"aura/router": "^3.3.0",
"aura/session": "^2.1.0",
"aviat/banker": "^4.1.2",
"aviat/query": "^4.1.0",
"ext-dom": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-pdo": "*",
"laminas/laminas-diactoros": "^2.0.0",
"laminas/laminas-httphandlerrunner": "^1.0",
"maximebf/consolekit": "^1.0",
"monolog/monolog": "^2.0.1",
"php": "^7.3",
"psr/container": "~1.0",
"psr/http-message": "~1.0",
"psr/log": "~1.0",
"yosymfony/toml": "^1.0"
"laminas/laminas-diactoros": "^3.0.0",
"laminas/laminas-httphandlerrunner": "^2.6.1",
"maximebf/consolekit": "^1.0.3",
"monolog/monolog": "^3.0.0",
"php": ">= 8.2.0",
"psr/http-message": "^1.0.1 || ^2.0.0",
"symfony/polyfill-mbstring": "^1.0.0",
"symfony/polyfill-util": "^1.0.0",
"tracy/tracy": "^2.8.0",
"yosymfony/toml": "^1.0.4"
},
"require-dev": {
"consolidation/robo": "^2.0.0",
"filp/whoops": "^2.1",
"pdepend/pdepend": "^2.2",
"phploc/phploc": "^5.0",
"phpmd/phpmd": "^2.8",
"phpstan/phpstan": "^0.12.0",
"phpunit/phpunit": "^8.4.3",
"phpstan/phpstan": "^1.2.0",
"phpunit/phpunit": "^10.0.0",
"roave/security-advisories": "dev-master",
"robmorgan/phinx": "^0.10.6",
"sebastian/phpcpd": "^4.1.0",
"spatie/phpunit-snapshot-assertions": "^2.2.1",
"squizlabs/php_codesniffer": "^3.2.2",
"symfony/var-dumper": "^5",
"theseer/phpdox": "*"
"spatie/phpunit-snapshot-assertions": "^5.0.1"
},
"scripts": {
"build": "vendor/bin/robo build",
"build:css": "cd public && npm run build:css && cd ..",
"build:js": "cd public && npm run build:js && cd ..",
"clean": "vendor/bin/robo clean",
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
"coverage": "php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude=\"~vendor~\" ./vendor/bin/phpunit -c build",
"phpstan": "phpstan analyse -c phpstan.neon",
"watch:css": "cd public && npm run watch:css",
"watch:js": "cd public && npm run watch:js",
@ -85,9 +70,7 @@
"test-update": "vendor/bin/phpunit -c build --no-coverage -d --update-snapshots"
},
"scripts-descriptions": {
"build": "Generate the api docs",
"build:css": "Generate browser css",
"clean": "Remove documentation generation files and folders",
"coverage": "Generate a test coverage report",
"phpstan": "Run PHP Static analysis",
"test": "Run the unit tests"

11
console
View File

@ -7,7 +7,10 @@ require_once __DIR__ . '/vendor/autoload.php';
use Aviat\AnimeClient\Command;
use ConsoleKit\Console;
$_SERVER['HTTP_HOST'] = 'localhost';
$GLOBALS['_SERVER']['HTTP_HOST'] = 'localhost';
const APP_DIR = __DIR__ . '/app';
const TEMPLATE_DIR = APP_DIR . '/templates';
// -----------------------------------------------------------------------------
// Start console script
@ -15,17 +18,15 @@ $_SERVER['HTTP_HOST'] = 'localhost';
try
{
(new Console([
'cache:clear' => Command\CacheClear::class,
'cache:refresh' => Command\CachePrime::class,
'clear:cache' => Command\CacheClear::class,
'clear:thumbnails' => Command\ClearThumbnails::class,
'refresh:cache' => Command\CachePrime::class,
'refresh:thumbnails' => Command\UpdateThumbnails::class,
'regenerate-thumbnails' => Command\UpdateThumbnails::class,
'lists:sync' => Command\SyncLists::class,
'sync:lists' => Command\SyncLists::class
]))->run();
}
catch (\Exception $e)
catch (Throwable)
{
}

70
frontEndSrc/css.js Normal file
View File

@ -0,0 +1,70 @@
/**
* Script for optimizing css
*/
const fs = require('fs');
const postcss = require('postcss');
const atImport = require('postcss-import');
const cssNext = require('postcss-preset-env');
const cssNano = require('cssnano');
const lightCss = fs.readFileSync('css/light.css', 'utf-8');
const darkCss = fs.readFileSync('css/src/dark-override.css', 'utf-8');
const fullDarkCss = fs.readFileSync('css/dark.css', 'utf-8');
const minOptions = {
autoprefixer: false,
colormin: false,
minifyFontValues: false,
options: {
sourcemap: false
}
};
const processOptions = {
browser: '> 0.5%',
features: {
'custom-properties': true,
},
stage: 0,
};
try {
(async () => {
// Basic theme
const lightMin = await postcss()
.use(atImport())
.use(cssNext(processOptions))
.use(cssNano(minOptions))
.process(lightCss, {
from: 'css/light.css',
to: '/public/css/light.min.css',
}).catch(console.error);
fs.writeFileSync('../public/css/light.min.css', lightMin.css);
// Dark theme
const darkFullMin = await postcss()
.use(atImport())
.use(cssNext(processOptions))
.use(cssNano(minOptions))
.process(fullDarkCss, {
from: 'css/dark.css',
to: '/public/css/dark.min.css',
});
fs.writeFileSync('../public/css/dark.min.css', darkFullMin.css);
// Dark override
const darkMin = await postcss()
.use(atImport())
.use(cssNext(processOptions))
.use(cssNano(minOptions))
.process(darkCss, {
from: 'css/dark-override.css',
to: '/public/css/dark.min.css',
}).catch(console.error);
const autoDarkCss = `${lightMin} @media (prefers-color-scheme: dark) { ${darkMin.css} }`
fs.writeFileSync('../public/css/auto.min.css', autoDarkCss)
})();
} catch (e) {
console.error(e)
}

3
frontEndSrc/css/auto.css Normal file
View File

@ -0,0 +1,3 @@
@media (prefers-color-scheme: dark) {
@import "src/dark-override.css";
}

5
frontEndSrc/css/dark.css Normal file
View File

@ -0,0 +1,5 @@
@import "src/-marx-.css";
@import "src/general.css";
@import "src/components.css";
@import "src/responsive.css";
@import "src/dark-override.css";

View File

@ -0,0 +1,4 @@
@import "src/-marx-.css";
@import "src/general.css";
@import "src/components.css";
@import "src/responsive.css";

View File

@ -163,7 +163,7 @@ CSS Tabs
/* text-align: center; */
}
.tabs .content {
.tabs .content, .single-tab {
display: none;
max-height: 950px;
border: 1px solid #e5e5e5;
@ -175,7 +175,14 @@ CSS Tabs
overflow: auto;
}
.tabs .content.full-height {
.single-tab {
display: block;
border: 1px solid #e5e5e5;
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
margin-top: 1.5em;
}
.tabs .content.full-height, .single-tab.full-height {
max-height: none;
}

View File

@ -147,7 +147,8 @@ button:active {
.tabs > [type="radio"]:checked + label,
.tabs > [type="radio"]:checked + label + .content,
.vertical-tabs [type="radio"]:checked + label,
.vertical-tabs [type="radio"]:checked ~ .content {
.vertical-tabs [type="radio"]:checked ~ .content,
.single-tab {
/* border-color: #333; */
border: 0;
background: #666;

View File

@ -94,6 +94,7 @@ a:hover, a:active {
iframe {
display: block;
margin: 0 auto;
border: 0;
}
/* -----------------------------------------------------------------------------
@ -332,7 +333,8 @@ td.danger, td.danger:hover, td.danger:active {
.borderless th,
.invisible tr,
.invisible td,
.invisible th {
.invisible th,
table.invisible {
box-shadow: none;
border: 0;
}
@ -627,6 +629,14 @@ picture.cover {
background-size: contain;
background-repeat: no-repeat;
}
/* There are two .name elements, just darken them both in this case! */
.media.search.disabled .name {
background-color: #000;
background-color: rgba(0, 0, 0, 0.75);
background-size: cover;
background-size: contain;
background-repeat: no-repeat;
}
.media.search > .row {
z-index: 6;
@ -827,19 +837,11 @@ aside.info {
max-width: 390px;
}
/* .fixed aside.info + article {
max-width: inherit;
} */
aside picture, aside img {
display: block;
margin: 0 auto;
}
/* aside.info + article {
max-width: 66%;
} */
/* ----------------------------------------------------------------------------
User page styles
-----------------------------------------------------------------------------*/
@ -873,6 +875,12 @@ aside picture, aside img {
vertical-align: middle;
}
.small-streaming-logo {
width: 25px;
height: 25px;
vertical-align: middle;
}
.cover-streaming-link {
display: none;
}
@ -888,6 +896,11 @@ aside picture, aside img {
filter: drop-shadow(0 -1px 4px #fff);
}
.history-img {
width: 110px;
height: 156px;
}
/* ----------------------------------------------------------------------------
Settings Form
-----------------------------------------------------------------------------*/

View File

@ -3,13 +3,13 @@
// -------------------------------------------------------------------------
const matches = (elm, selector) => {
let matches = (elm.document || elm.ownerDocument).querySelectorAll(selector),
i = matches.length;
while (--i >= 0 && matches.item(i) !== elm) {};
let m = (elm.document || elm.ownerDocument).querySelectorAll(selector);
let i = matches.length;
while (--i >= 0 && m.item(i) !== elm) {};
return i > -1;
}
export const AnimeClient = {
const AnimeClient = {
/**
* Placeholder function
*/
@ -18,8 +18,8 @@ export const AnimeClient = {
* DOM selector
*
* @param {string} selector - The dom selector string
* @param {object} [context]
* @return {[HTMLElement]} - array of dom elements
* @param {Element} [context]
* @return array of dom elements
*/
$(selector, context = null) {
if (typeof selector !== 'string') {
@ -60,7 +60,7 @@ export const AnimeClient = {
/**
* Hide the selected element
*
* @param {string|Element} sel - the selector of the element to hide
* @param {string|Element|Element[]} sel - the selector of the element to hide
* @return {void}
*/
hide (sel) {
@ -77,7 +77,7 @@ export const AnimeClient = {
/**
* UnHide the selected element
*
* @param {string|Element} sel - the selector of the element to hide
* @param {string|Element|Element[]} sel - the selector of the element to hide
* @return {void}
*/
show (sel) {
@ -116,9 +116,9 @@ export const AnimeClient = {
/**
* Finds the closest parent element matching the passed selector
*
* @param {HTMLElement} current - the current HTMLElement
* @param {Element} current - the current Element
* @param {string} parentSelector - selector for the parent element
* @return {HTMLElement|null} - the parent element
* @return {Element|null} - the parent element
*/
closestParent (current, parentSelector) {
if (Element.prototype.closest !== undefined) {
@ -204,9 +204,9 @@ function delegateEvent(sel, target, event, listener) {
/**
* Add an event listener
*
* @param {string|HTMLElement} sel - the parent selector to bind to
* @param {string|Element} sel - the parent selector to bind to
* @param {string} event - event name(s) to bind
* @param {string|HTMLElement|function} target - the element to directly bind the event to
* @param {string|Element|function} target - the element to directly bind the event to
* @param {function} [listener] - event listener callback
* @return {void}
*/
@ -261,7 +261,7 @@ function ajaxSerialize(data) {
*
* @param {string} url - the url to request
* @param {Object} config - the configuration object
* @return {void}
* @return {XMLHttpRequest}
*/
AnimeClient.ajax = (url, config) => {
// Set some sane defaults
@ -322,6 +322,8 @@ AnimeClient.ajax = (url, config) => {
} else {
request.send(config.data);
}
return request
};
/**
@ -330,6 +332,7 @@ AnimeClient.ajax = (url, config) => {
* @param {string} url
* @param {object|function} data
* @param {function} [callback]
* @return {XMLHttpRequest}
*/
AnimeClient.get = (url, data, callback = null) => {
if (callback === null) {

128
frontEndSrc/js/anime.js Normal file
View File

@ -0,0 +1,128 @@
import _ from './anime-client.js'
import { renderSearchResults } from './template-helpers.js'
import { getNestedProperty, hasNestedProperty } from "./fns";
const search = (query, isCollection = false) => {
// Show the loader
_.show('.cssload-loader');
// Do the api search
return _.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
searchResults = JSON.parse(searchResults);
// Hide the loader
_.hide('.cssload-loader');
// Show the results
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults, isCollection);
});
};
// Anime list search
if (_.hasElement('.anime #search')) {
let prevRequest = null;
_.on('#search', 'input', _.throttle(250, (e) => {
const query = encodeURIComponent(e.target.value);
if (query === '') {
return;
}
if (prevRequest !== null) {
prevRequest.abort();
}
prevRequest = search(query);
}));
}
// Anime collection search
if (_.hasElement('#search-anime-collection')) {
let prevRequest = null;
_.on('#search-anime-collection', 'input', _.throttle(250, (e) => {
const query = encodeURIComponent(e.target.value);
if (query === '') {
return;
}
if (prevRequest !== null) {
prevRequest.abort();
}
prevRequest = search(query, true);
}));
}
// Action to increment episode count
_.on('body.anime.list', 'click', '.plus-one', (e) => {
let parentSel = _.closestParent(e.target, 'article');
let watchedCount = parseInt(_.$('.completed_number', parentSel)[ 0 ].textContent, 10) || 0;
let totalCount = parseInt(_.$('.total_number', parentSel)[ 0 ].textContent, 10);
let title = _.$('.name a', parentSel)[ 0 ].textContent;
// Setup the update data
let data = {
id: parentSel.dataset.kitsuId,
anilist_id: parentSel.dataset.anilistId,
mal_id: parentSel.dataset.malId,
data: {
progress: watchedCount + 1
}
};
const displayMessage = (type, message) => {
_.hide('#loading-shadow');
_.showMessage(type, `${message} ${title}`);
_.scrollToTop();
}
const showError = () => displayMessage('error', 'Failed to update');
// If the episode count is 0, and incremented,
// change status to currently watching
if (isNaN(watchedCount) || watchedCount === 0) {
data.data.status = 'CURRENT';
}
// If you increment at the last episode, mark as completed
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
data.data.status = 'COMPLETED';
}
_.show('#loading-shadow');
// okay, lets actually make some changes!
_.ajax(_.url('/anime/increment'), {
data,
dataType: 'json',
type: 'POST',
success: (res) => {
try {
const resData = JSON.parse(res);
// Do a rough sanity check for weird errors
let updatedProgress = getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.progress');
if (hasNestedProperty(resData, 'error') || updatedProgress !== data.data.progress) {
showError();
return;
}
// We've completed the series
if (getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.status') === 'COMPLETED') {
_.hide(parentSel);
displayMessage('success', 'Completed')
return;
}
// Just a normal update
_.$('.completed_number', parentSel)[ 0 ].textContent = ++watchedCount;
displayMessage('success', 'Updated');
} catch (_) {
showError();
}
},
error: showError,
});
});

View File

@ -6,9 +6,22 @@ const LightTableSorter = (() => {
const sort = (a, b) => {
let textA = text(a);
let textB = text(b);
const n = parseInt(textA, 10);
if (n) {
textA = n;
console.log("Comparing " + textA + " and " + textB)
if(th.classList.contains("numeric")){
let arrayA = textA.replace('episodes: ','').replace('-',0).split("/");
let arrayB = textB.replace('episodes: ','').replace('-',0).split("/");
if(arrayA.length > 1) {
textA = parseInt(arrayA[0],10) / parseInt(arrayA[1],10);
textB = parseInt(arrayB[0],10) / parseInt(arrayB[1],10);
}
else{
textA = parseInt(arrayA[0],10);
textB = parseInt(arrayB[0],10);
}
}
else if (parseInt(textA, 10)) {
textA = parseInt(textA, 10);
textB = parseInt(textB, 10);
}
if (textA > textB) {
@ -59,6 +72,7 @@ const LightTableSorter = (() => {
for (let i = 0, len = ths.length; i < len; i++) {
let th = ths[i];
th.classList.add('sorting');
th.classList.add('testing');
results.push(th.onclick = onClickEvent);
}
return results;

Some files were not shown because too many files have changed in this diff Show More