From d31191732e41aa106234b5ebe6e54ee02f0ce603 Mon Sep 17 00:00:00 2001
From: Benjamin Bellamy <ben@podlibre.org>
Date: Sun, 14 Jun 2020 15:45:42 +0000
Subject: [PATCH] fix: move analytics to helper

---
 app/Controllers/Analytics.php      | 115 ++++-------------
 app/Controllers/BaseController.php |  52 +-------
 app/Helpers/analytics_helper.php   | 191 +++++++++++++++++++++++++++++
 app/Helpers/url_helper.php         |  20 +++
 app/Views/episodes/view.php        |  10 +-
 app/Views/podcasts/view.php        |  10 +-
 6 files changed, 256 insertions(+), 142 deletions(-)
 create mode 100644 app/Helpers/analytics_helper.php

diff --git a/app/Controllers/Analytics.php b/app/Controllers/Analytics.php
index 76bf580bbd..f001a45a56 100644
--- a/app/Controllers/Analytics.php
+++ b/app/Controllers/Analytics.php
@@ -11,103 +11,40 @@ use CodeIgniter\Controller;
 
 class Analytics extends Controller
 {
-    function __construct()
-    {
-        $session = \Config\Services::session();
-        $session->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);
-        }
+    /**
+     * An array of helpers to be loaded automatically upon
+     * class instantiation. These helpers will be available
+     * to all other controllers that extend Analytics.
+     *
+     * @var array
+     */
+    protected $helpers = ['analytics'];
 
-        // Finds player:
-        if (!$session->has('player')) {
-            $playerName = '-unknown-';
-            $error = '';
+    /**
+     * Constructor.
+     */
+    public function initController(
+        \CodeIgniter\HTTP\RequestInterface $request,
+        \CodeIgniter\HTTP\ResponseInterface $response,
+        \Psr\Log\LoggerInterface $logger
+    ) {
+        // Do Not Edit This Line
+        parent::initController($request, $response, $logger);
 
-            try {
-                $useragent = $_SERVER['HTTP_USER_AGENT'];
-                $jsonUserAgents = json_decode(
-                    file_get_contents(
-                        WRITEPATH . 'uploads/user-agents/src/user-agents.json'
-                    ),
-                    true
-                );
+        //--------------------------------------------------------------------
+        // Preload any models, libraries, etc, here.
+        //--------------------------------------------------------------------
+        // E.g.:
+        // $this->session = \Config\Services::session();
 
-                //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);
-        }
+        set_user_session_country();
+        set_user_session_player();
     }
 
     // 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
-        }
+        podcast_hit($p_podcast_id, $p_episode_id);
         return redirect()->to(media_url(implode('/', $filename)));
     }
 }
diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php
index 834792c1b3..e2400f352a 100644
--- a/app/Controllers/BaseController.php
+++ b/app/Controllers/BaseController.php
@@ -26,7 +26,7 @@ class BaseController extends Controller
      *
      * @var array
      */
-    protected $helpers = [];
+    protected $helpers = ['analytics'];
 
     /**
      * Constructor.
@@ -45,55 +45,13 @@ 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);
-        }
+        set_user_session_country();
+        set_user_session_browser();
+        set_user_session_referer();
     }
 
     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'),
-        ]);
+        webpage_hit($postcast_id);
     }
 }
diff --git a/app/Helpers/analytics_helper.php b/app/Helpers/analytics_helper.php
new file mode 100644
index 0000000000..409731fbc3
--- /dev/null
+++ b/app/Helpers/analytics_helper.php
@@ -0,0 +1,191 @@
+
+<?php
+/**
+ * @copyright  2020 Podlibre
+ * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
+ * @link       https://castopod.org/
+ */
+
+/**
+ * Set user country in session variable, for analytics purpose
+ */
+function set_user_session_country()
+{
+    $session = \Config\Services::session();
+    $session->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);
+    }
+}
+
+/**
+ * Set user player in session variable, for analytics purpose
+ */
+function set_user_session_player()
+{
+    $session = \Config\Services::session();
+    $session->start();
+
+    if (!$session->has('player')) {
+        $session = \Config\Services::session();
+        $session->start();
+
+        $playerName = '- Unknown Player -';
+
+        $useragent = $_SERVER['HTTP_USER_AGENT'];
+
+        try {
+            $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 Player -') {
+            // Add to unknown list
+            try {
+                $db = \Config\Database::connect();
+                $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);
+    }
+}
+
+/**
+ * Set user browser in session variable, for analytics purpose
+ */
+function set_user_session_browser()
+{
+    $session = \Config\Services::session();
+    $session->start();
+
+    if (!$session->has('browser')) {
+        $browserName = '- Other -';
+        try {
+            $whichbrowser = new \WhichBrowser\Parser(getallheaders());
+            $browserName = $whichbrowser->browser->name;
+        } catch (\Exception $e) {
+            $browserName = '- Could not get browser name -';
+        }
+        if ($browserName == null) {
+            $browserName = '- Could not get browser name -';
+        }
+        $session->set('browser', $browserName);
+    }
+}
+
+/**
+ * Set user referer in session variable, for analytics purpose
+ */
+function set_user_session_referer()
+{
+    $session = \Config\Services::session();
+    $session->start();
+
+    $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);
+    }
+}
+
+function webpage_hit($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'),
+    ]);
+}
+
+function podcast_hit($p_podcast_id, $p_episode_id)
+{
+    $session = \Config\Services::session();
+    $session->start();
+    $first_time_for_this_episode = true;
+
+    if ($session->has('episodes')) {
+        if (in_array($p_episode_id, $session->get('episodes'))) {
+            $first_time_for_this_episode = false;
+        } else {
+            $session->push('episodes', [$p_episode_id]);
+        }
+    } else {
+        $session->set('episodes', [$p_episode_id]);
+    }
+
+    if ($first_time_for_this_episode) {
+        $db = \Config\Database::connect();
+        $procedureName = $db->prefixTable('analytics_podcasts');
+        try {
+            $db->query("CALL $procedureName(?,?,?,?);", [
+                $p_podcast_id,
+                $p_episode_id,
+                $session->get('country'),
+                $session->get('player'),
+            ]);
+        } catch (\Exception $e) {
+            // If things go wrong the show must go on and the user must be able to download the file
+        }
+    }
+}
+
diff --git a/app/Helpers/url_helper.php b/app/Helpers/url_helper.php
index 5b3ca930e4..858f3de72e 100644
--- a/app/Helpers/url_helper.php
+++ b/app/Helpers/url_helper.php
@@ -16,3 +16,23 @@ function media_url($uri = '', string $protocol = null): string
 {
     return base_url(config('App')->mediaRoot . '/' . $uri, $protocol);
 }
+
+/**
+ * Return the podcast URL to use in views
+ *
+ * @param  mixed  $uri      URI string or array of URI segments
+ * @param  string $protocol
+ * @return string
+ */
+function podcast_url(
+    $podcast_id = 1,
+    $episode_id = 1,
+    $podcast_name = '',
+    $uri = '',
+    string $protocol = null
+): string {
+    return base_url(
+        "/stats/$podcast_id/$episode_id/$podcast_name/$uri",
+        $protocol
+    );
+}
diff --git a/app/Views/episodes/view.php b/app/Views/episodes/view.php
index 3f9f8073ee..63a3a7b750 100644
--- a/app/Views/episodes/view.php
+++ b/app/Views/episodes/view.php
@@ -6,11 +6,15 @@
 <img src="<?= media_url(
     $episode->image ? $episode->image : $podcast->image
 ) ?>" alt="Episode cover"  class="object-cover w-40 h-40 mb-6" />
-<audio controls>
-  <source src="<?= media_url(
+<audio controls preload="none">
+  <source src="<?= podcast_url(
+      $episode->podcast_id,
+      $episode->id,
+      $podcast->name,
       $episode->enclosure_url
   ) ?>" type="<?= $episode->enclosure_type ?>">
   Your browser does not support the audio tag.
 </audio>
 
-<?= $this->endSection() ?>
+<?= $this->endSection()
+?>
diff --git a/app/Views/podcasts/view.php b/app/Views/podcasts/view.php
index e97939d089..34641d4a18 100644
--- a/app/Views/podcasts/view.php
+++ b/app/Views/podcasts/view.php
@@ -34,8 +34,11 @@
                         </h3>
                         <p><?= $episode->description ?></p>
                     </a>
-                    <audio controls class="mt-auto">
-                        <source src="<?= media_url(
+                    <audio controls class="mt-auto" preload="none">
+                        <source src="<?= podcast_url(
+                            $episode->podcast_id,
+                            $episode->id,
+                            $podcast->name,
                             $episode->enclosure_url
                         ) ?>" type="<?= $episode->enclosure_type ?>">
                         Your browser does not support the audio tag.
@@ -49,4 +52,5 @@
 </section>
 
 
-<?= $this->endSection() ?>
+<?= $this->endSection()
+?>
-- 
GitLab