Skip to content
Snippets Groups Projects
Commit 7202b986 authored by Benjamin Bellamy's avatar Benjamin Bellamy :speech_balloon:
Browse files

feat(analytics): add service name from rss user-agent

BREAKING CHANGE: analytics_podcasts_by_player table and analytics_podcasts procedure were updated
parent 8ca5b33b
No related branches found
No related tags found
No related merge requests found
Showing
with 200 additions and 105 deletions
...@@ -55,7 +55,15 @@ class Analytics extends Controller ...@@ -55,7 +55,15 @@ class Analytics extends Controller
) { ) {
helper('media'); helper('media');
podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize); $serviceName = isset($_GET['s']) ? $_GET['s'] : '';
podcast_hit(
$podcastId,
$episodeId,
$bytesThreshold,
$fileSize,
$serviceName
);
return redirect()->to(media_url(implode('/', $filename))); return redirect()->to(media_url(implode('/', $filename)));
} }
} }
...@@ -15,13 +15,32 @@ class Feed extends Controller ...@@ -15,13 +15,32 @@ class Feed extends Controller
{ {
public function index($podcastName) public function index($podcastName)
{ {
// The page cache is set to a decade so it is deleted manually upon podcast update
$this->cachePage(DECADE);
helper('rss'); helper('rss');
$podcast = (new PodcastModel())->where('name', $podcastName)->first(); $podcast = (new PodcastModel())->where('name', $podcastName)->first();
if (!$podcast) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
return $this->response->setXML(get_rss_feed($podcast)); $service = null;
try {
$service = \Opawg\UserAgentsPhp\UserAgentsRSS::find(
$_SERVER['HTTP_USER_AGENT']
);
} catch (\Exception $e) {
// If things go wrong the show must go on and the user must be able to download the file
log_message('critical', $e);
}
$cacheName =
"podcast{$podcast->id}_feed" .
($service ? "_{$service['slug']}" : '');
if (!($found = cache($cacheName))) {
$found = get_rss_feed(
$podcast,
$service ? '?s=' . urlencode($service['name']) : ''
);
cache()->save($cacheName, $found, DECADE);
}
return $this->response->setXML($found);
} }
} }
...@@ -25,6 +25,10 @@ class AddAnalyticsPodcastsByPlayer extends Migration ...@@ -25,6 +25,10 @@ class AddAnalyticsPodcastsByPlayer extends Migration
'date' => [ 'date' => [
'type' => 'date', 'type' => 'date',
], ],
'service' => [
'type' => 'VARCHAR',
'constraint' => 128,
],
'app' => [ 'app' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 128, 'constraint' => 128,
...@@ -51,6 +55,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration ...@@ -51,6 +55,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration
$this->forge->addPrimaryKey([ $this->forge->addPrimaryKey([
'podcast_id', 'podcast_id',
'date', 'date',
'service',
'app', 'app',
'device', 'device',
'os', 'os',
......
...@@ -28,6 +28,7 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` ( ...@@ -28,6 +28,7 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` (
IN `p_region_code` VARCHAR(3) CHARSET utf8mb4, IN `p_region_code` VARCHAR(3) CHARSET utf8mb4,
IN `p_latitude` FLOAT, IN `p_latitude` FLOAT,
IN `p_longitude` FLOAT, IN `p_longitude` FLOAT,
IN `p_service` VARCHAR(128) CHARSET utf8mb4,
IN `p_app` VARCHAR(128) CHARSET utf8mb4, IN `p_app` VARCHAR(128) CHARSET utf8mb4,
IN `p_device` VARCHAR(32) CHARSET utf8mb4, IN `p_device` VARCHAR(32) CHARSET utf8mb4,
IN `p_os` VARCHAR(32) CHARSET utf8mb4, IN `p_os` VARCHAR(32) CHARSET utf8mb4,
...@@ -52,8 +53,8 @@ IF NOT `p_bot` THEN ...@@ -52,8 +53,8 @@ IF NOT `p_bot` THEN
VALUES (p_podcast_id, p_country_code, p_region_code, p_latitude, p_longitude, DATE(NOW())) VALUES (p_podcast_id, p_country_code, p_region_code, p_latitude, p_longitude, DATE(NOW()))
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END IF; END IF;
INSERT INTO `{$prefix}analytics_podcasts_by_player`(`podcast_id`, `app`, `device`, `os`, `bot`, `date`) INSERT INTO `{$prefix}analytics_podcasts_by_player`(`podcast_id`, `service`, `app`, `device`, `os`, `bot`, `date`)
VALUES (p_podcast_id, p_app, p_device, p_os, p_bot, DATE(NOW())) VALUES (p_podcast_id, p_service, p_app, p_device, p_os, p_bot, DATE(NOW()))
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END END
EOD; EOD;
......
...@@ -102,7 +102,7 @@ function set_user_session_player() ...@@ -102,7 +102,7 @@ function set_user_session_player()
$userAgent = $_SERVER['HTTP_USER_AGENT']; $userAgent = $_SERVER['HTTP_USER_AGENT'];
try { try {
$playerFound = \Podlibre\UserAgentsPhp\UserAgents::find($userAgent); $playerFound = \Opawg\UserAgentsPhp\UserAgents::find($userAgent);
} catch (\Exception $e) { } catch (\Exception $e) {
// If things go wrong the show must go on and the user must be able to download the file // If things go wrong the show must go on and the user must be able to download the file
} }
...@@ -227,6 +227,7 @@ function webpage_hit($podcast_id) ...@@ -227,6 +227,7 @@ function webpage_hit($podcast_id)
* ✅ Castopod does not do pre-load * ✅ Castopod does not do pre-load
* ✅ IP deny list https://github.com/client9/ipcat * ✅ IP deny list https://github.com/client9/ipcat
* ✅ User-agent Filtering https://github.com/opawg/user-agents * ✅ User-agent Filtering https://github.com/opawg/user-agents
* ✅ RSS User-agent https://github.com/opawg/podcast-rss-useragents
* ✅ Ignores 2 bytes range "Range: 0-1" (performed by official Apple iOS Podcast app) * ✅ Ignores 2 bytes range "Range: 0-1" (performed by official Apple iOS Podcast app)
* ✅ In case of partial content, adds up all requests to check >1mn was downloaded * ✅ In case of partial content, adds up all requests to check >1mn was downloaded
* ✅ Identifying Uniques is done with a combination of IP Address and User Agent * ✅ Identifying Uniques is done with a combination of IP Address and User Agent
...@@ -234,11 +235,17 @@ function webpage_hit($podcast_id) ...@@ -234,11 +235,17 @@ function webpage_hit($podcast_id)
* @param int $episodeId The Episode ID * @param int $episodeId The Episode ID
* @param int $bytesThreshold The minimum total number of bytes that must be downloaded so that an episode is counted (>1mn) * @param int $bytesThreshold The minimum total number of bytes that must be downloaded so that an episode is counted (>1mn)
* @param int $fileSize The podcast complete file size * @param int $fileSize The podcast complete file size
* @param string $serviceName The name of the service that had fetched the RSS feed
* *
* @return void * @return void
*/ */
function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize) function podcast_hit(
{ $podcastId,
$episodeId,
$bytesThreshold,
$fileSize,
$serviceName
) {
$session = \Config\Services::session(); $session = \Config\Services::session();
$session->start(); $session->start();
...@@ -328,22 +335,18 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize) ...@@ -328,22 +335,18 @@ function podcast_hit($podcastId, $episodeId, $bytesThreshold, $fileSize)
// We save the download count for this user until midnight: // We save the download count for this user until midnight:
cache()->save($listenerHashId, $downloadsByUser, $midnightTTL); cache()->save($listenerHashId, $downloadsByUser, $midnightTTL);
$app = $session->get('player')['app']; $db->query("CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?);", [
$device = $session->get('player')['device'];
$os = $session->get('player')['os'];
$bot = $session->get('player')['bot'];
$db->query("CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?);", [
$podcastId, $podcastId,
$episodeId, $episodeId,
$session->get('location')['countryCode'], $session->get('location')['countryCode'],
$session->get('location')['regionCode'], $session->get('location')['regionCode'],
$session->get('location')['latitude'], $session->get('location')['latitude'],
$session->get('location')['longitude'], $session->get('location')['longitude'],
$app == null ? '' : $app, $serviceName,
$device == null ? '' : $device, $session->get('player')['app'],
$os == null ? '' : $os, $session->get('player')['device'],
$bot == null ? 0 : $bot, $session->get('player')['os'],
$session->get('player')['bot'],
$newListener, $newListener,
]); ]);
} }
......
...@@ -13,9 +13,10 @@ use CodeIgniter\I18n\Time; ...@@ -13,9 +13,10 @@ use CodeIgniter\I18n\Time;
* Generates the rss feed for a given podcast entity * Generates the rss feed for a given podcast entity
* *
* @param App\Entities\Podcast $podcast * @param App\Entities\Podcast $podcast
* @param string $service The name of the service that fetches the RSS feed for future reference when the audio file is eventually downloaded
* @return string rss feed as xml * @return string rss feed as xml
*/ */
function get_rss_feed($podcast) function get_rss_feed($podcast, $serviceName = '')
{ {
$episodes = $podcast->episodes; $episodes = $podcast->episodes;
...@@ -102,7 +103,7 @@ function get_rss_feed($podcast) ...@@ -102,7 +103,7 @@ function get_rss_feed($podcast)
$item->addChild('title', $episode->title); $item->addChild('title', $episode->title);
$enclosure = $item->addChild('enclosure'); $enclosure = $item->addChild('enclosure');
$enclosure->addAttribute('url', $episode->enclosure_url); $enclosure->addAttribute('url', $episode->enclosure_url . $serviceName);
$enclosure->addAttribute('length', $episode->enclosure_filesize); $enclosure->addAttribute('length', $episode->enclosure_filesize);
$enclosure->addAttribute('type', $episode->enclosure_mimetype); $enclosure->addAttribute('type', $episode->enclosure_mimetype);
......
...@@ -7,22 +7,23 @@ ...@@ -7,22 +7,23 @@
*/ */
return [ return [
'by_player_weekly' => 'Podcast downloads by player (for the past week)', 'by_service_weekly' => 'Episode downloads by service (for the past week)',
'by_player_yearly' => 'Podcast downloads by player (for the past year)', 'by_player_weekly' => 'Episode downloads by player (for the past week)',
'by_device_weekly' => 'Podcast downloads by device (for the past week)', 'by_player_yearly' => 'Episode downloads by player (for the past year)',
'by_os_weekly' => 'Podcast downloads by O.S. (for the past week)', 'by_device_weekly' => 'Episode downloads by device (for the past week)',
'podcast_by_region' => 'Podcast downloads by region (for the past week)', 'by_os_weekly' => 'Episode downloads by O.S. (for the past week)',
'podcast_by_region' => 'Episode downloads by region (for the past week)',
'unique_daily_listeners' => 'Daily unique listeners', 'unique_daily_listeners' => 'Daily unique listeners',
'unique_monthly_listeners' => 'Monthly unique listeners', 'unique_monthly_listeners' => 'Monthly unique listeners',
'by_browser' => 'Web pages usage by browser (for the past week)', 'by_browser' => 'Web pages usage by browser (for the past week)',
'podcast_by_day' => 'Podcast daily downloads', 'podcast_by_day' => 'Episode daily downloads',
'podcast_by_month' => 'Podcast monthly downloads', 'podcast_by_month' => 'Episode monthly downloads',
'episode_by_day' => 'Episode daily downloads (first 60 days)', 'episode_by_day' => 'Episode daily downloads (first 60 days)',
'episode_by_month' => 'Episode monthly downloads', 'episode_by_month' => 'Episode monthly downloads',
'episodes_by_day' => 'episodes_by_day' =>
'5 latest episodes downloads (during their first 60 days)', '5 latest episodes downloads (during their first 60 days)',
'by_country_weekly' => 'Podcast downloads by country (for the past week)', 'by_country_weekly' => 'Episode downloads by country (for the past week)',
'by_country_yearly' => 'Podcast downloads by country (for the past year)', 'by_country_yearly' => 'Episode downloads by country (for the past year)',
'by_domain_weekly' => 'Web pages visits by source (for the past week)', 'by_domain_weekly' => 'Web pages visits by source (for the past week)',
'by_domain_yearly' => 'Web pages visits by source (for the past year)', 'by_domain_yearly' => 'Web pages visits by source (for the past year)',
'by_entry_page' => 'Web pages visits by landing page (for the past week)', 'by_entry_page' => 'Web pages visits by landing page (for the past week)',
......
...@@ -7,31 +7,33 @@ ...@@ -7,31 +7,33 @@
*/ */
return [ return [
'by_service_weekly' =>
'Téléchargements d’épisodes par service (sur la dernière semaine)',
'by_player_weekly' => 'by_player_weekly' =>
'Téléchargements de Podcast par lecteur (sur la dernière semaine)', 'Téléchargements d’épisodes par lecteur (sur la dernière semaine)',
'by_player_yearly' => 'by_player_yearly' =>
'Téléchargements de Podcast par lecteur (sur la dernière année)', 'Téléchargements d’épisodes par lecteur (sur la dernière année)',
'by_device_weekly' => 'by_device_weekly' =>
'Téléchargements de Podcast par appareil (sur la dernière semaine)', 'Téléchargements d’épisodes par appareil (sur la dernière semaine)',
'by_os_weekly' => 'by_os_weekly' =>
'Téléchargements de Podcast par OS (sur la dernière semaine)', 'Téléchargements d’épisodes par OS (sur la dernière semaine)',
'podcast_by_region' => 'podcast_by_region' =>
'Téléchargements de Podcast par région (sur la dernière semaine)', 'Téléchargements d’épisodes par région (sur la dernière semaine)',
'unique_daily_listeners' => 'Auditeurs uniques quotidiens', 'unique_daily_listeners' => 'Auditeurs uniques quotidiens',
'unique_monthly_listeners' => 'Auditeurs uniques mensuels', 'unique_monthly_listeners' => 'Auditeurs uniques mensuels',
'by_browser' => 'by_browser' =>
'Fréquentation des pages web par navigateur (sur la dernière semaine)', 'Fréquentation des pages web par navigateur (sur la dernière semaine)',
'podcast_by_day' => 'Téléchargements quotidiens de podcasts', 'podcast_by_day' => 'Téléchargements quotidiens d’épisodes',
'podcast_by_month' => 'Téléchargements mensuels de podcasts', 'podcast_by_month' => 'Téléchargements mensuels d’épisodes',
'episode_by_day' => 'episode_by_day' =>
'Téléchargements quotidiens de l’épisode (sur les 60 premiers jours)', 'Téléchargements quotidiens de l’épisode (sur les 60 premiers jours)',
'episode_by_month' => 'Téléchargements mensuels de l’épisode', 'episode_by_month' => 'Téléchargements mensuels de l’épisode',
'episodes_by_day' => 'episodes_by_day' =>
'Téléchargements des 5 derniers épisodes (sur les 60 premiers jours)', 'Téléchargements des 5 derniers épisodes (sur les 60 premiers jours)',
'by_country_weekly' => 'by_country_weekly' =>
'Téléchargement de podcasts par pays (sur la dernière semaine)', 'Téléchargement d’épisodes par pays (sur la dernière semaine)',
'by_country_yearly' => 'by_country_yearly' =>
'Téléchargement de podcasts par pays (sur la dernière année)', 'Téléchargement d’épisodes par pays (sur la dernière année)',
'by_domain_weekly' => 'by_domain_weekly' =>
'Fréquentation des pages web par origine (sur la dernière semaine)', 'Fréquentation des pages web par origine (sur la dernière semaine)',
'by_domain_yearly' => 'by_domain_yearly' =>
......
...@@ -23,6 +23,41 @@ class AnalyticsPodcastByPlayerModel extends Model ...@@ -23,6 +23,41 @@ class AnalyticsPodcastByPlayerModel extends Model
protected $useTimestamps = false; protected $useTimestamps = false;
/**
* Gets service data for a podcast
*
* @param int $podcastId
*
* @return array
*/
public function getDataByServiceWeekly(int $podcastId): array
{
if (
!($found = cache(
"{$podcastId}_analytics_podcasts_by_player_by_service_weekly"
))
) {
$found = $this->select('`service` as `labels`')
->selectSum('`hits`', '`values`')
->where([
'`podcast_id`' => $podcastId,
'`service` !=' => '',
'`bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 week')),
])
->groupBy('`labels`')
->orderBy('`values`', 'DESC')
->findAll(10);
cache()->save(
"{$podcastId}_analytics_podcasts_by_player_by_service_weekly",
$found,
600
);
}
return $found;
}
/** /**
* Gets player data for a podcast * Gets player data for a podcast
* *
......
...@@ -272,7 +272,12 @@ class EpisodeModel extends Model ...@@ -272,7 +272,12 @@ class EpisodeModel extends Model
); );
// delete cache for rss feed // delete cache for rss feed
cache()->delete(md5($episode->podcast->feed_url)); cache()->delete("podcast{$episode->podcast_id}_feed");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete(
"podcast{$episode->podcast_id}_feed_{$service['slug']}"
);
}
// delete model requests cache // delete model requests cache
cache()->delete("podcast{$episode->podcast_id}_episodes"); cache()->delete("podcast{$episode->podcast_id}_episodes");
......
...@@ -173,7 +173,10 @@ class PodcastModel extends Model ...@@ -173,7 +173,10 @@ class PodcastModel extends Model
$supportedLocales = config('App')->supportedLocales; $supportedLocales = config('App')->supportedLocales;
// delete cache for rss feed and podcast pages // delete cache for rss feed and podcast pages
cache()->delete(md5($podcast->feed_url)); cache()->delete("podcast{$podcast->id}_feed");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete("podcast{$podcast->id}_feed_{$service['slug']}");
}
// delete model requests cache // delete model requests cache
cache()->delete("podcast{$podcast->id}"); cache()->delete("podcast{$podcast->id}");
......
...@@ -21,14 +21,13 @@ ...@@ -21,14 +21,13 @@
) ?>"></div> ) ?>"></div>
</div> </div>
<div class="mb-12 mr-6 text-center"> <div class="mb-12 mr-6 text-center">
<h2><?= lang('Charts.by_player_yearly') ?></h2> <h2><?= lang('Charts.by_service_weekly') ?></h2>
<div class="chart-pie" id="by-app-yearly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to( <div class="chart-pie" id="by-service-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByPlayer', 'PodcastByPlayer',
'ByAppYearly' 'ByServiceWeekly'
) ?>"></div> ) ?>"></div>
</div> </div>
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
'id' => 'image', 'id' => 'image',
'name' => 'image', 'name' => 'image',
'class' => 'form-input', 'class' => 'form-input',
'required' => 'required', 'required' => 'required',
'type' => 'file', 'type' => 'file',
'accept' => '.jpg,.jpeg,.png', 'accept' => '.jpg,.jpeg,.png',
...@@ -58,21 +59,27 @@ ...@@ -58,21 +59,27 @@
'required' => 'required', 'required' => 'required',
]) ?> ]) ?>
<?= form_fieldset('', [ <?= form_fieldset('', ['class' => 'mb-4']) ?>
'class' => 'mb-4',
]) ?>
<legend> <legend>
<?= lang('Podcast.form.type.label') . <?= lang('Podcast.form.type.label') .
hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?> hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
</legend> </legend>
<?= form_radio( <?= form_radio(
['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'], [
'id' => 'episodic',
'name' => 'type',
'class' => 'form-radio-btn',
],
'episodic', 'episodic',
old('type') ? old('type') == 'episodic' : true old('type') ? old('type') == 'episodic' : true
) ?> ) ?>
<label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label> <label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
<?= form_radio( <?= form_radio(
['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'], [
'id' => 'serial',
'name' => 'type',
'class' => 'form-radio-btn',
],
'serial', 'serial',
old('type') ? old('type') == 'serial' : false old('type') ? old('type') == 'serial' : false
) ?> ) ?>
...@@ -252,6 +259,7 @@ ...@@ -252,6 +259,7 @@
['id' => 'block', 'name' => 'block'], ['id' => 'block', 'name' => 'block'],
'yes', 'yes',
old('block', false), old('block', false),
'mb-2' 'mb-2'
) ?> ) ?>
...@@ -266,7 +274,7 @@ ...@@ -266,7 +274,7 @@
lang('Podcast.form.lock'), lang('Podcast.form.lock'),
['id' => 'lock', 'name' => 'lock'], ['id' => 'lock', 'name' => 'lock'],
'yes', 'yes',
old('lock', $podcast->lock) old('lock', true)
) ?> ) ?>
<?= form_section_close() ?> <?= form_section_close() ?>
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
"league/commonmark": "^1.5", "league/commonmark": "^1.5",
"vlucas/phpdotenv": "^5.2", "vlucas/phpdotenv": "^5.2",
"league/html-to-markdown": "^4.10", "league/html-to-markdown": "^4.10",
"podlibre/user-agents-php": "*", "opawg/user-agents-php": "*",
"podlibre/ipcat": "*" "podlibre/ipcat": "*"
}, },
"require-dev": { "require-dev": {
...@@ -31,12 +31,14 @@ ...@@ -31,12 +31,14 @@
"scripts": { "scripts": {
"test": "phpunit", "test": "phpunit",
"post-install-cmd": [ "post-install-cmd": [
"@php vendor/podlibre/user-agents-php/src/UserAgentsGenerate.php > vendor/podlibre/user-agents-php/src/UserAgents.php", "@php vendor/opawg/user-agents-php/src/UserAgentsGenerate.php > vendor/opawg/user-agents-php/src/UserAgents.php",
"@php vendor/opawg/user-agents-php/src/UserAgentsRSSGenerate.php > vendor/opawg/user-agents-php/src/UserAgentsRSS.php",
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php" "@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php"
], ],
"post-update-cmd": [ "post-update-cmd": [
"@composer dump-autoload", "@composer dump-autoload",
"@php vendor/podlibre/user-agents-php/src/UserAgentsGenerate.php > vendor/podlibre/user-agents-php/src/UserAgents.php", "@php vendor/opawg/user-agents-php/src/UserAgentsGenerate.php > vendor/opawg/user-agents-php/src/UserAgents.php",
"@php vendor/opawg/user-agents-php/src/UserAgentsRSSGenerate.php > vendor/opawg/user-agents-php/src/UserAgentsRSS.php",
"@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php" "@php vendor/podlibre/ipcat/IpDbGenerate.php > vendor/podlibre/ipcat/IpDb.php"
] ]
}, },
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "47b9f628f03f8c494a9339b054359ec8", "content-hash": "37551523e4097a9341bc00dd317f573d",
"packages": [ "packages": [
{ {
"name": "codeigniter4/codeigniter4", "name": "codeigniter4/codeigniter4",
...@@ -12,12 +12,12 @@ ...@@ -12,12 +12,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/codeigniter4/CodeIgniter4.git", "url": "https://github.com/codeigniter4/CodeIgniter4.git",
"reference": "f5545aa7274575c397efae4ebcf6c18779dcf895" "reference": "13ff147fa4cd9db15888b041ef35bc22ed94252a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/f5545aa7274575c397efae4ebcf6c18779dcf895", "url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/13ff147fa4cd9db15888b041ef35bc22ed94252a",
"reference": "f5545aa7274575c397efae4ebcf6c18779dcf895", "reference": "13ff147fa4cd9db15888b041ef35bc22ed94252a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
"slack": "https://codeigniterchat.slack.com", "slack": "https://codeigniterchat.slack.com",
"issues": "https://github.com/codeigniter4/CodeIgniter4/issues" "issues": "https://github.com/codeigniter4/CodeIgniter4/issues"
}, },
"time": "2020-10-06T06:38:58+00:00" "time": "2020-10-20T18:13:11+00:00"
}, },
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
...@@ -518,16 +518,16 @@ ...@@ -518,16 +518,16 @@
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "1.5.5", "version": "1.5.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/commonmark.git", "url": "https://github.com/thephpleague/commonmark.git",
"reference": "45832dfed6007b984c0d40addfac48d403dc6432" "reference": "a56e91e0fa1f6d0049153a9c34f63488f6b7ce61"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/45832dfed6007b984c0d40addfac48d403dc6432", "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/a56e91e0fa1f6d0049153a9c34f63488f6b7ce61",
"reference": "45832dfed6007b984c0d40addfac48d403dc6432", "reference": "a56e91e0fa1f6d0049153a9c34f63488f6b7ce61",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -609,7 +609,7 @@ ...@@ -609,7 +609,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2020-09-13T14:44:46+00:00" "time": "2020-10-17T21:33:03+00:00"
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
...@@ -805,12 +805,12 @@ ...@@ -805,12 +805,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/lonnieezell/myth-auth.git", "url": "https://github.com/lonnieezell/myth-auth.git",
"reference": "e838cb8de6ffa118caf2b9909e71776a866c8973" "reference": "e9d6a2f557bd275158e0b84624534b2abeeb539c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/e838cb8de6ffa118caf2b9909e71776a866c8973", "url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/e9d6a2f557bd275158e0b84624534b2abeeb539c",
"reference": "e838cb8de6ffa118caf2b9909e71776a866c8973", "reference": "e9d6a2f557bd275158e0b84624534b2abeeb539c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -818,9 +818,12 @@ ...@@ -818,9 +818,12 @@
}, },
"require-dev": { "require-dev": {
"codeigniter4/codeigniter4": "dev-develop", "codeigniter4/codeigniter4": "dev-develop",
"codeigniter4/codeigniter4-standard": "^1.0",
"fzaninotto/faker": "^1.9@dev", "fzaninotto/faker": "^1.9@dev",
"mockery/mockery": "^1.0", "mockery/mockery": "^1.0",
"phpunit/phpunit": "8.5.*" "phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^8.5",
"squizlabs/php_codesniffer": "^3.5"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
...@@ -857,7 +860,42 @@ ...@@ -857,7 +860,42 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2020-09-07T03:37:26+00:00" "time": "2020-10-16T18:51:37+00:00"
},
{
"name": "opawg/user-agents-php",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/opawg/user-agents-php.git",
"reference": "3b71eeed2c3216f1c1c361c62d4d3a7002be0481"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opawg/user-agents-php/zipball/3b71eeed2c3216f1c1c361c62d4d3a7002be0481",
"reference": "3b71eeed2c3216f1c1c361c62d4d3a7002be0481",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Opawg\\UserAgentsPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Benjamin Bellamy",
"email": "ben@podlibre.org",
"homepage": "https://podlibre.org/"
}
],
"description": "PHP implementation for opawg/user-agents.",
"homepage": "https://github.com/opawg/user-agents-php",
"time": "2020-10-20T23:22:20+00:00"
}, },
{ {
"name": "phpoption/phpoption", "name": "phpoption/phpoption",
...@@ -959,41 +997,6 @@ ...@@ -959,41 +997,6 @@
"homepage": "https://github.com/podlibre/ipcat", "homepage": "https://github.com/podlibre/ipcat",
"time": "2020-10-05T17:15:07+00:00" "time": "2020-10-05T17:15:07+00:00"
}, },
{
"name": "podlibre/user-agents-php",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/podlibre/user-agents-php.git",
"reference": "891066bae6b4881a8b7a57eb72a67fca1fcf67c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/podlibre/user-agents-php/zipball/891066bae6b4881a8b7a57eb72a67fca1fcf67c0",
"reference": "891066bae6b4881a8b7a57eb72a67fca1fcf67c0",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Podlibre\\UserAgentsPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Benjamin Bellamy",
"email": "ben@podlibre.org",
"homepage": "https://podlibre.org/"
}
],
"description": "PHP implementation for opawg/user-agents.",
"homepage": "https://github.com/podlibre/user-agents-php",
"time": "2020-10-05T16:58:13+00:00"
},
{ {
"name": "psr/cache", "name": "psr/cache",
"version": "1.0.1", "version": "1.0.1",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment