From ec92e65aa42e09b1df04600b52a0c679dfc494bb Mon Sep 17 00:00:00 2001 From: Benjamin Bellamy Date: Fri, 12 Jun 2020 20:41:09 +0000 Subject: [PATCH] feat: add analytics and unknown useragents --- app/Config/Routes.php | 7 + app/Controllers/Analytics.php | 113 + app/Controllers/BaseController.php | 51 + app/Controllers/Episodes.php | 1 + app/Controllers/Podcasts.php | 1 + app/Controllers/UnknownUserAgents.php | 19 + ... 2020-06-08-160000_add_platform_links.php} | 0 ...0000_add_analytics_episodes_by_country.php | 75 + ...10000_add_analytics_episodes_by_player.php | 75 + ...0000_add_analytics_podcasts_by_country.php | 63 + ...10000_add_analytics_podcasts_by_player.php | 63 + ...10000_add_analytics_unknown_useragents.php | 53 + ...10000_add_analytics_website_by_browser.php | 63 + ...10000_add_analytics_website_by_country.php | 63 + ...10000_add_analytics_website_by_referer.php | 63 + ...dd_analytics_podcasts_stored_procedure.php | 49 + ...cs_unknown_useragents_stored_procedure.php | 37 + ...add_analytics_website_stored_procedure.php | 45 + app/Entities/AnalyticsEpisodesByCountry.php | 22 + app/Entities/AnalyticsEpisodesByPlayer.php | 22 + app/Entities/AnalyticsPodcastsByCountry.php | 21 + app/Entities/AnalyticsPodcastsByPlayer.php | 21 + app/Entities/AnalyticsUnknownUseragents.php | 19 + app/Entities/AnalyticsWebsiteByBrowser.php | 21 + app/Entities/AnalyticsWebsiteByCountry.php | 21 + app/Entities/AnalyticsWebsiteByReferer.php | 21 + app/Language/en/Countries.php | 1 - app/Language/fr/Countries.php | 1 - .../AnalyticsEpisodesByCountryModel.php | 24 + app/Models/AnalyticsEpisodesByPlayerModel.php | 24 + .../AnalyticsPodcastsByCountryModel.php | 24 + app/Models/AnalyticsPodcastsByPlayerModel.php | 24 + .../AnalyticsUnknownUseragentsModel.php | 24 + app/Models/AnalyticsWebsiteByBrowserModel.php | 24 + app/Models/AnalyticsWebsiteByCountryModel.php | 24 + app/Models/AnalyticsWebsiteByRefererModel.php | 24 + app/Models/UnknownUserAgentsModel.php | 23 + app/Views/errors/cli/error_exception.php | 24 +- app/Views/errors/html/error_404.php | 6 +- app/Views/errors/html/error_exception.php | 164 +- app/Views/errors/html/production.php | 6 +- app/Views/json/unknownuseragents.php | 5 + composer.json | 4 +- composer.lock | 4007 +++++++++-------- 44 files changed, 3394 insertions(+), 2048 deletions(-) create mode 100644 app/Controllers/Analytics.php create mode 100644 app/Controllers/UnknownUserAgents.php rename app/Database/Migrations/{2020-06-08-160000_add_platformlinks.php => 2020-06-08-160000_add_platform_links.php} (100%) create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_country.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_player.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_country.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_player.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_unknown_useragents.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_browser.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_country.php create mode 100644 app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_referer.php create mode 100644 app/Database/Migrations/2020-06-11-210000_add_analytics_podcasts_stored_procedure.php create mode 100644 app/Database/Migrations/2020-06-11-210000_add_analytics_unknown_useragents_stored_procedure.php create mode 100644 app/Database/Migrations/2020-06-11-210000_add_analytics_website_stored_procedure.php create mode 100644 app/Entities/AnalyticsEpisodesByCountry.php create mode 100644 app/Entities/AnalyticsEpisodesByPlayer.php create mode 100644 app/Entities/AnalyticsPodcastsByCountry.php create mode 100644 app/Entities/AnalyticsPodcastsByPlayer.php create mode 100644 app/Entities/AnalyticsUnknownUseragents.php create mode 100644 app/Entities/AnalyticsWebsiteByBrowser.php create mode 100644 app/Entities/AnalyticsWebsiteByCountry.php create mode 100644 app/Entities/AnalyticsWebsiteByReferer.php create mode 100644 app/Models/AnalyticsEpisodesByCountryModel.php create mode 100644 app/Models/AnalyticsEpisodesByPlayerModel.php create mode 100644 app/Models/AnalyticsPodcastsByCountryModel.php create mode 100644 app/Models/AnalyticsPodcastsByPlayerModel.php create mode 100644 app/Models/AnalyticsUnknownUseragentsModel.php create mode 100644 app/Models/AnalyticsWebsiteByBrowserModel.php create mode 100644 app/Models/AnalyticsWebsiteByCountryModel.php create mode 100644 app/Models/AnalyticsWebsiteByRefererModel.php create mode 100644 app/Models/UnknownUserAgentsModel.php create mode 100644 app/Views/json/unknownuseragents.php diff --git a/app/Config/Routes.php b/app/Config/Routes.php index dbdb7097..f87b1d60 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -46,6 +46,13 @@ $routes->group('(:podcastSlug)', function ($routes) { ]); }); +// Route for podcast audio file analytics (/stats/podcast_id/episode_id/podcast_folder/filename.mp3) +$routes->add('/stats/(:num)/(:num)/(:any)', 'Analytics::hit/$1/$2/$3'); + +// Show the Unknown UserAgents +$routes->add('/.well-known/unknown-useragents', 'UnknownUserAgents'); +$routes->add('/.well-known/unknown-useragents/(:num)', 'UnknownUserAgents/$1'); + /** * -------------------------------------------------------------------- * Additional Routing diff --git a/app/Controllers/Analytics.php b/app/Controllers/Analytics.php new file mode 100644 index 00000000..76bf580b --- /dev/null +++ b/app/Controllers/Analytics.php @@ -0,0 +1,113 @@ +start(); + $db = \Config\Database::connect(); + $country = 'N/A'; + + // Finds country: + if (!$session->has('country')) { + try { + $reader = new \GeoIp2\Database\Reader( + WRITEPATH . 'uploads/GeoLite2-Country/GeoLite2-Country.mmdb' + ); + $geoip = $reader->country($_SERVER['REMOTE_ADDR']); + $country = $geoip->country->isoCode; + } catch (\Exception $e) { + // If things go wrong the show must go on and the user must be able to download the file + } + $session->set('country', $country); + } + + // Finds player: + if (!$session->has('player')) { + $playerName = '-unknown-'; + $error = ''; + + try { + $useragent = $_SERVER['HTTP_USER_AGENT']; + $jsonUserAgents = json_decode( + file_get_contents( + WRITEPATH . 'uploads/user-agents/src/user-agents.json' + ), + true + ); + + //Search for current HTTP_USER_AGENT in json file: + foreach ($jsonUserAgents as $player) { + foreach ($player['user_agents'] as $useragentsRegexp) { + //Does the HTTP_USER_AGENT match this regexp: + if (preg_match("#{$useragentsRegexp}#", $useragent)) { + if (isset($player['bot'])) { + //It’s a bot! + $playerName = '-bot-'; + } else { + //It isn’t a bot, we store device/os/app: + $playerName = + (isset($player['device']) + ? $player['device'] . '/' + : '') . + (isset($player['os']) + ? $player['os'] . '/' + : '') . + (isset($player['app']) + ? $player['app'] + : '?'); + } + //We found it! + break 2; + } + } + } + } catch (\Exception $e) { + // If things go wrong the show must go on and the user must be able to download the file + } + if ($playerName == '-unknown-') { + // Add to unknown list + try { + $procedureNameAUU = $db->prefixTable( + 'analytics_unknown_useragents' + ); + $db->query("CALL $procedureNameAUU(?)", [$useragent]); + } catch (\Exception $e) { + // If things go wrong the show must go on and the user must be able to download the file + } + } + $session->set('player', $playerName); + } + } + + // Add one hit to this episode: + public function hit($p_podcast_id, $p_episode_id, ...$filename) + { + $session = \Config\Services::session(); + $db = \Config\Database::connect(); + $procedureName = $db->prefixTable('analytics_podcasts'); + $p_country_code = $session->get('country'); + $p_player = $session->get('player'); + try { + $db->query("CALL $procedureName(?,?,?,?);", [ + $p_podcast_id, + $p_episode_id, + $p_country_code, + $p_player, + ]); + } catch (\Exception $e) { + // If things go wrong the show must go on and the user must be able to download the file + } + return redirect()->to(media_url(implode('/', $filename))); + } +} diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index 22c55eb7..834792c1 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -44,5 +44,56 @@ class BaseController extends Controller //-------------------------------------------------------------------- // E.g.: // $this->session = \Config\Services::session(); + + $session = \Config\Services::session(); + $session->start(); + + // Defines country + if (!$session->has('country')) { + try { + $reader = new \GeoIp2\Database\Reader( + WRITEPATH . 'uploads/GeoLite2-Country/GeoLite2-Country.mmdb' + ); + $geoip = $reader->country($_SERVER['REMOTE_ADDR']); + $session->set('country', $geoip->country->isoCode); + } catch (\Exception $e) { + $session->set('country', 'N/A'); + } + } + // Defines browser + if (!$session->has('browser')) { + try { + $whichbrowser = new \WhichBrowser\Parser(getallheaders()); + $session->set('browser', $whichbrowser->browser->name); + } catch (\Exception $e) { + $session->set('browser', 'Other'); + } + } + + // Defines referrer + $newreferer = isset($_SERVER['HTTP_REFERER']) + ? parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) + : '- Direct -'; + $newreferer = + $newreferer == parse_url(current_url(false), PHP_URL_HOST) + ? '- Direct -' + : $newreferer; + if (!$session->has('referer') or $newreferer != '- Direct -') { + $session->set('referer', $newreferer); + } + } + + protected function stats($postcast_id) + { + $session = \Config\Services::session(); + $session->start(); + $db = \Config\Database::connect(); + $procedureName = $db->prefixTable('analytics_website'); + $db->query("call $procedureName(?,?,?,?)", [ + $postcast_id, + $session->get('country'), + $session->get('browser'), + $session->get('referer'), + ]); } } diff --git a/app/Controllers/Episodes.php b/app/Controllers/Episodes.php index df0d1cb1..90b1d5e7 100644 --- a/app/Controllers/Episodes.php +++ b/app/Controllers/Episodes.php @@ -125,6 +125,7 @@ class Episodes extends BaseController ->first(), 'episode' => $episode_model->where('slug', $episode_slug)->first(), ]; + self::stats($data['podcast']->id); return view('episodes/view.php', $data); } diff --git a/app/Controllers/Podcasts.php b/app/Controllers/Podcasts.php index e190eb28..eb29154e 100644 --- a/app/Controllers/Podcasts.php +++ b/app/Controllers/Podcasts.php @@ -96,6 +96,7 @@ class Podcasts extends BaseController ->where('podcast_id', $podcast->id) ->findAll(), ]; + self::stats($podcast->id); return view('podcasts/view', $data); } diff --git a/app/Controllers/UnknownUserAgents.php b/app/Controllers/UnknownUserAgents.php new file mode 100644 index 00000000..220daf86 --- /dev/null +++ b/app/Controllers/UnknownUserAgents.php @@ -0,0 +1,19 @@ + $model->getUserAgents($p_id), + ]; + + $this->response->setContentType('application/json'); + $this->response->setStatusCode(\CodeIgniter\HTTP\Response::HTTP_OK); + + echo view('json/unknownuseragents', $data); + } +} diff --git a/app/Database/Migrations/2020-06-08-160000_add_platformlinks.php b/app/Database/Migrations/2020-06-08-160000_add_platform_links.php similarity index 100% rename from app/Database/Migrations/2020-06-08-160000_add_platformlinks.php rename to app/Database/Migrations/2020-06-08-160000_add_platform_links.php diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_country.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_country.php new file mode 100644 index 00000000..4c421769 --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_country.php @@ -0,0 +1,75 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'episode_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The episode ID', + ], + 'country_code' => [ + 'type' => 'VARCHAR', + 'constraint' => 3, + 'comment' => 'ISO 3166-1 code.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey([ + 'podcast_id', + 'episode_id', + 'country_code', + 'date', + ]); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->addForeignKey('episode_id', 'episodes', 'id'); + $this->forge->createTable('analytics_episodes_by_country'); + } + + public function down() + { + $this->forge->dropTable('analytics_episodes_by_country'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_player.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_player.php new file mode 100644 index 00000000..89cead45 --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_episodes_by_player.php @@ -0,0 +1,75 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'episode_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The episode ID', + ], + 'player' => [ + 'type' => 'VARCHAR', + 'constraint' => 191, + 'comment' => 'Podcast player name.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey([ + 'podcast_id', + 'episode_id', + 'player', + 'date', + ]); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->addForeignKey('episode_id', 'episodes', 'id'); + $this->forge->createTable('analytics_episodes_by_player'); + } + + public function down() + { + $this->forge->dropTable('analytics_episodes_by_player'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_country.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_country.php new file mode 100644 index 00000000..72cc32b0 --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_country.php @@ -0,0 +1,63 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'country_code' => [ + 'type' => 'VARCHAR', + 'constraint' => 3, + 'comment' => 'ISO 3166-1 code.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey(['podcast_id', 'country_code', 'date']); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->createTable('analytics_podcasts_by_country'); + } + + public function down() + { + $this->forge->dropTable('analytics_podcasts_by_country'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_player.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_player.php new file mode 100644 index 00000000..5cbaca75 --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_podcasts_by_player.php @@ -0,0 +1,63 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'player' => [ + 'type' => 'VARCHAR', + 'constraint' => 191, + 'comment' => 'Podcast player name.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey(['podcast_id', 'player', 'date']); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->createTable('analytics_podcasts_by_player'); + } + + public function down() + { + $this->forge->dropTable('analytics_podcasts_by_player'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_unknown_useragents.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_unknown_useragents.php new file mode 100644 index 00000000..8960323c --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_unknown_useragents.php @@ -0,0 +1,53 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'useragent' => [ + 'type' => 'VARCHAR', + 'constraint' => 191, + 'unique' => true, + 'comment' => 'The unknown user-agent.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + // `created_at` and `updated_at` are created with SQL because Model class won’t be used for insertion (Stored Procedure will be used instead) + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->createTable('analytics_unknown_useragents'); + } + + public function down() + { + $this->forge->dropTable('analytics_unknown_useragents'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_browser.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_browser.php new file mode 100644 index 00000000..7c676391 --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_browser.php @@ -0,0 +1,63 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'browser' => [ + 'type' => 'VARCHAR', + 'constraint' => 191, + 'comment' => 'The Web Browser.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey(['podcast_id', 'browser', 'date']); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->createTable('analytics_website_by_browser'); + } + + public function down() + { + $this->forge->dropTable('analytics_website_by_browser'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_country.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_country.php new file mode 100644 index 00000000..a531781e --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_country.php @@ -0,0 +1,63 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'country_code' => [ + 'type' => 'VARCHAR', + 'constraint' => 3, + 'comment' => 'ISO 3166-1 code.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey(['podcast_id', 'country_code', 'date']); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->createTable('analytics_website_by_country'); + } + + public function down() + { + $this->forge->dropTable('analytics_website_by_country'); + } +} diff --git a/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_referer.php b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_referer.php new file mode 100644 index 00000000..4c5f27fc --- /dev/null +++ b/app/Database/Migrations/2020-06-08-210000_add_analytics_website_by_referer.php @@ -0,0 +1,63 @@ +forge->addField([ + 'id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'auto_increment' => true, + 'comment' => 'The line ID', + ], + 'podcast_id' => [ + 'type' => 'BIGINT', + 'constraint' => 20, + 'unsigned' => true, + 'comment' => 'The podcast ID', + ], + 'referer' => [ + 'type' => 'VARCHAR', + 'constraint' => 191, + 'comment' => 'Referer URL.', + ], + 'date' => [ + 'type' => 'date', + 'comment' => 'Line date.', + ], + 'hits' => [ + 'type' => 'INT', + 'constraint' => 10, + 'default' => 1, + 'comment' => 'Number of hits.', + ], + ]); + $this->forge->addKey('id', true); + $this->forge->addUniqueKey(['podcast_id', 'referer', 'date']); + $this->forge->addField( + '`created_at` timestamp NOT NULL DEFAULT current_timestamp()' + ); + $this->forge->addField( + '`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()' + ); + $this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); + $this->forge->createTable('analytics_website_by_referer'); + } + + public function down() + { + $this->forge->dropTable('analytics_website_by_referer'); + } +} diff --git a/app/Database/Migrations/2020-06-11-210000_add_analytics_podcasts_stored_procedure.php b/app/Database/Migrations/2020-06-11-210000_add_analytics_podcasts_stored_procedure.php new file mode 100644 index 00000000..ab327728 --- /dev/null +++ b/app/Database/Migrations/2020-06-11-210000_add_analytics_podcasts_stored_procedure.php @@ -0,0 +1,49 @@ +db->prefixTable('analytics_podcasts'); + $episodesTableName = $this->db->prefixTable('analytics_episodes'); + $createQuery = <<db->query($createQuery); + } + + public function down() + { + $procedureName = $this->db->prefixTable('analytics_podcasts'); + $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`"); + } +} diff --git a/app/Database/Migrations/2020-06-11-210000_add_analytics_unknown_useragents_stored_procedure.php b/app/Database/Migrations/2020-06-11-210000_add_analytics_unknown_useragents_stored_procedure.php new file mode 100644 index 00000000..683078b9 --- /dev/null +++ b/app/Database/Migrations/2020-06-11-210000_add_analytics_unknown_useragents_stored_procedure.php @@ -0,0 +1,37 @@ +db->prefixTable('analytics_unknown_useragents'); + $createQuery = <<db->query($createQuery); + } + + public function down() + { + $procedureName = $this->db->prefixTable('analytics_unknown_useragents'); + $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`"); + } +} diff --git a/app/Database/Migrations/2020-06-11-210000_add_analytics_website_stored_procedure.php b/app/Database/Migrations/2020-06-11-210000_add_analytics_website_stored_procedure.php new file mode 100644 index 00000000..e66739cd --- /dev/null +++ b/app/Database/Migrations/2020-06-11-210000_add_analytics_website_stored_procedure.php @@ -0,0 +1,45 @@ +db->prefixTable('analytics_website'); + $createQuery = <<db->query($createQuery); + } + + public function down() + { + $procedureName = $this->db->prefixTable('analytics_website'); + $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`"); + } +} diff --git a/app/Entities/AnalyticsEpisodesByCountry.php b/app/Entities/AnalyticsEpisodesByCountry.php new file mode 100644 index 00000000..b22542b2 --- /dev/null +++ b/app/Entities/AnalyticsEpisodesByCountry.php @@ -0,0 +1,22 @@ + 'integer', + 'episode_id' => 'integer', + 'country_code' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsEpisodesByPlayer.php b/app/Entities/AnalyticsEpisodesByPlayer.php new file mode 100644 index 00000000..35a55182 --- /dev/null +++ b/app/Entities/AnalyticsEpisodesByPlayer.php @@ -0,0 +1,22 @@ + 'integer', + 'episode_id' => 'integer', + 'player' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsPodcastsByCountry.php b/app/Entities/AnalyticsPodcastsByCountry.php new file mode 100644 index 00000000..dcdfe037 --- /dev/null +++ b/app/Entities/AnalyticsPodcastsByCountry.php @@ -0,0 +1,21 @@ + 'integer', + 'country_code' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsPodcastsByPlayer.php b/app/Entities/AnalyticsPodcastsByPlayer.php new file mode 100644 index 00000000..4b2d5227 --- /dev/null +++ b/app/Entities/AnalyticsPodcastsByPlayer.php @@ -0,0 +1,21 @@ + 'integer', + 'player' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsUnknownUseragents.php b/app/Entities/AnalyticsUnknownUseragents.php new file mode 100644 index 00000000..2aee6c1b --- /dev/null +++ b/app/Entities/AnalyticsUnknownUseragents.php @@ -0,0 +1,19 @@ + 'integer', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsWebsiteByBrowser.php b/app/Entities/AnalyticsWebsiteByBrowser.php new file mode 100644 index 00000000..0ade4dcf --- /dev/null +++ b/app/Entities/AnalyticsWebsiteByBrowser.php @@ -0,0 +1,21 @@ + 'integer', + 'browser' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsWebsiteByCountry.php b/app/Entities/AnalyticsWebsiteByCountry.php new file mode 100644 index 00000000..996c61d0 --- /dev/null +++ b/app/Entities/AnalyticsWebsiteByCountry.php @@ -0,0 +1,21 @@ + 'integer', + 'country_code' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Entities/AnalyticsWebsiteByReferer.php b/app/Entities/AnalyticsWebsiteByReferer.php new file mode 100644 index 00000000..0244076c --- /dev/null +++ b/app/Entities/AnalyticsWebsiteByReferer.php @@ -0,0 +1,21 @@ + 'integer', + 'referer' => 'string', + 'date' => 'datetime', + 'hits' => 'integer', + ]; +} diff --git a/app/Language/en/Countries.php b/app/Language/en/Countries.php index 1e91d66f..9bd81808 100644 --- a/app/Language/en/Countries.php +++ b/app/Language/en/Countries.php @@ -1,7 +1,6 @@ * @copyright 2020 Podlibre * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ diff --git a/app/Language/fr/Countries.php b/app/Language/fr/Countries.php index b9cd78e3..8b25fff7 100644 --- a/app/Language/fr/Countries.php +++ b/app/Language/fr/Countries.php @@ -1,7 +1,6 @@ * @copyright 2020 Podlibre * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ diff --git a/app/Models/AnalyticsEpisodesByCountryModel.php b/app/Models/AnalyticsEpisodesByCountryModel.php new file mode 100644 index 00000000..c43e3d34 --- /dev/null +++ b/app/Models/AnalyticsEpisodesByCountryModel.php @@ -0,0 +1,24 @@ +where('id>', $p_id)->findAll(); + } +} diff --git a/app/Views/errors/cli/error_exception.php b/app/Views/errors/cli/error_exception.php index 6588932c..a91a3f6a 100644 --- a/app/Views/errors/cli/error_exception.php +++ b/app/Views/errors/cli/error_exception.php @@ -1,17 +1,19 @@ An uncaught Exception was encountered -Type: -Message: -Filename: getFile(), "\n"; ?> -Line Number: getLine(); ?> +Type: +Message: +Filename: getFile(), "\n" ?> +Line Number: getLine() ?> - + Backtrace: - getTrace() as $error) : ?> - - - - + getTrace() as $error): ?> + + + + - \ No newline at end of file + diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php index 2bb33f87..35017d33 100644 --- a/app/Views/errors/html/error_404.php +++ b/app/Views/errors/html/error_404.php @@ -83,11 +83,11 @@

404 - File Not Found

- + - + Sorry! Cannot seem to find the page you were looking for. - +

diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index dd6f6daf..7eb3736a 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -8,7 +8,11 @@ <?= htmlspecialchars($title, ENT_SUBSTITUTE, 'UTF-8') ?>