Unverified Commit 54b84f96 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

perf(cache): update CI4 to use cache's deleteMatching method

add missing locale to category_options cache name
parent 05ace8cf
......@@ -13,17 +13,18 @@
"color-highlight.markerType": "dot-before"
},
"extensions": [
"mikestead.dotenv",
"bmewburn.vscode-intelephense-client",
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"heybourn.headwind",
"wayou.vscode-todo-highlight",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"jamesbirtles.svelte-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"wongjn.php-sniffer"
]
"mikestead.dotenv",
"bmewburn.vscode-intelephense-client",
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"heybourn.headwind",
"wayou.vscode-todo-highlight",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"jamesbirtles.svelte-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"wongjn.php-sniffer",
"eamodio.gitlens"
]
}
......@@ -50,15 +50,16 @@ Before uploading Castopod files to your web server:
PHP version 7.3 or higher is required, with the following extensions installed:
- [intl](http://php.net/manual/en/intl.requirements.php)
- [libcurl](http://php.net/manual/en/curl.requirements.php)
- [mbstring](http://php.net/manual/en/mbstring.installation.php)
- [intl](https://php.net/manual/en/intl.requirements.php)
- [libcurl](https://php.net/manual/en/curl.requirements.php)
- [mbstring](https://php.net/manual/en/mbstring.installation.php)
- [gd](https://www.php.net/manual/en/image.installation.php)
Additionally, make sure that the following extensions are enabled in your PHP:
- json (enabled by default - don't turn it off)
- xml (enabled by default - don't turn it off)
- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php)
- [mysqlnd](https://php.net/manual/en/mysqlnd.install.php)
### MySQL compatible database
......
......@@ -39,14 +39,15 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
*/
public function getPermissionsForGroup(int $groupId): array
{
if (!($found = cache("group{$groupId}_permissions"))) {
$cacheName = "group{$groupId}_permissions";
if (!($found = cache($cacheName))) {
$groupPermissions = $this->db
->table('auth_groups_permissions')
->select('id, auth_permissions.name')
->join(
'auth_permissions',
'auth_permissions.id = permission_id',
'inner'
'inner',
)
->where('group_id', $groupId)
->get()
......@@ -57,7 +58,7 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel
$found[$row->id] = strtolower($row->name);
}
cache()->save("group{$groupId}_permissions", $found, 300);
cache()->save($cacheName, $found, 300);
}
return $found;
......
......@@ -253,6 +253,8 @@ class App extends BaseConfig
* Set a cookie name prefix if you need to avoid collisions.
*
* @var string
*
* @deprecated use Config\Cookie::$prefix property instead.
*/
public $cookiePrefix = '';
......@@ -264,6 +266,8 @@ class App extends BaseConfig
* Set to `.your-domain.com` for site-wide cookies.
*
* @var string
*
* @deprecated use Config\Cookie::$domain property instead.
*/
public $cookieDomain = '';
......@@ -275,6 +279,8 @@ class App extends BaseConfig
* Typically will be a forward slash.
*
* @var string
*
* @deprecated use Config\Cookie::$path property instead.
*/
public $cookiePath = '/';
......@@ -286,19 +292,23 @@ class App extends BaseConfig
* Cookie will only be set if a secure HTTPS connection exists.
*
* @var boolean
*
* @deprecated use Config\Cookie::$secure property instead.
*/
public $cookieSecure = false;
/**
* --------------------------------------------------------------------------
* Cookie HTTP Only
* Cookie HttpOnly
* --------------------------------------------------------------------------
*
* Cookie will only be accessible via HTTP(S) (no JavaScript).
*
* @var boolean
*
* @deprecated use Config\Cookie::$httponly property instead.
*/
public $cookieHTTPOnly = false;
public $cookieHTTPOnly = true;
/**
* --------------------------------------------------------------------------
......@@ -311,11 +321,18 @@ class App extends BaseConfig
* - Strict
* - ''
*
* Alternatively, you can use the constant names:
* - `Cookie::SAMESITE_NONE`
* - `Cookie::SAMESITE_LAX`
* - `Cookie::SAMESITE_STRICT`
*
* Defaults to `Lax` for compatibility with modern browsers. Setting `''`
* (empty string) means no SameSite attribute will be set on cookies. If
* set to `None`, `$cookieSecure` must also be set.
* (empty string) means default SameSite attribute set by browsers (`Lax`)
* will be set on cookies. If set to `None`, `$cookieSecure` must also be set.
*
* @var string
*
* @var string 'Lax'|'None'|'Strict'
* @deprecated use Config\Cookie::$samesite property instead.
*/
public $cookieSameSite = 'Lax';
......
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
use DateTimeInterface;
class Cookie extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Cookie Prefix
* --------------------------------------------------------------------------
*
* Set a cookie name prefix if you need to avoid collisions.
*
* @var string
*/
public $prefix = '';
/**
* --------------------------------------------------------------------------
* Cookie Expires Timestamp
* --------------------------------------------------------------------------
*
* Default expires timestamp for cookies. Setting this to `0` will mean the
* cookie will not have the `Expires` attribute and will behave as a session
* cookie.
*
* @var DateTimeInterface|integer|string
*/
public $expires = 0;
/**
* --------------------------------------------------------------------------
* Cookie Path
* --------------------------------------------------------------------------
*
* Typically will be a forward slash.
*
* @var string
*/
public $path = '/';
/**
* --------------------------------------------------------------------------
* Cookie Domain
* --------------------------------------------------------------------------
*
* Set to `.your-domain.com` for site-wide cookies.
*
* @var string
*/
public $domain = '';
/**
* --------------------------------------------------------------------------
* Cookie Secure
* --------------------------------------------------------------------------
*
* Cookie will only be set if a secure HTTPS connection exists.
*
* @var boolean
*/
public $secure = false;
/**
* --------------------------------------------------------------------------
* Cookie HTTPOnly
* --------------------------------------------------------------------------
*
* Cookie will only be accessible via HTTP(S) (no JavaScript).
*
* @var boolean
*/
public $httponly = true;
/**
* --------------------------------------------------------------------------
* Cookie SameSite
* --------------------------------------------------------------------------
*
* Configure cookie SameSite setting. Allowed values are:
* - None
* - Lax
* - Strict
* - ''
*
* Alternatively, you can use the constant names:
* - `Cookie::SAMESITE_NONE`
* - `Cookie::SAMESITE_LAX`
* - `Cookie::SAMESITE_STRICT`
*
* Defaults to `Lax` for compatibility with modern browsers. Setting `''`
* (empty string) means default SameSite attribute set by browsers (`Lax`)
* will be set on cookies. If set to `None`, `$secure` must also be set.
*
* @var string
*/
public $samesite = 'Lax';
/**
* --------------------------------------------------------------------------
* Cookie Raw
* --------------------------------------------------------------------------
*
* This flag allows setting a "raw" cookie, i.e., its name and value are
* not URL encoded using `rawurlencode()`.
*
* If this is set to `true`, cookie names should be compliant of RFC 2616's
* list of allowed characters.
*
* @var boolean
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
* @see https://tools.ietf.org/html/rfc2616#section-2.2
*/
public $raw = false;
}
......@@ -651,7 +651,7 @@ $routes->group('@(:podcastName)', function ($routes) {
// Other pages
$routes->get('/credits', 'Page::credits', ['as' => 'credits']);
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']);
$routes->get('/pages/(:slug)', 'Page/$1', ['as' => 'page']);
// interacting as an actor
$routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [
......
......@@ -30,7 +30,7 @@ class EpisodePerson extends BaseController
if (count($params) > 1) {
if (
!($this->podcast = (new PodcastModel())->getPodcastById(
$params[0]
$params[0],
))
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
......@@ -61,9 +61,9 @@ class EpisodePerson extends BaseController
$data = [
'episode' => $this->episode,
'podcast' => $this->podcast,
'episodePersons' => (new EpisodePersonModel())->getPersonsByEpisodeId(
'episodePersons' => (new EpisodePersonModel())->getEpisodePersons(
$this->podcast->id,
$this->episode->id
$this->episode->id,
),
'personOptions' => (new PersonModel())->getPersonOptions(),
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
......@@ -92,7 +92,7 @@ class EpisodePerson extends BaseController
$this->podcast->id,
$this->episode->id,
$this->request->getPost('person'),
$this->request->getPost('person_group_role')
$this->request->getPost('person_group_role'),
);
return redirect()->back();
......@@ -103,7 +103,7 @@ class EpisodePerson extends BaseController
(new EpisodePersonModel())->removeEpisodePersons(
$this->podcast->id,
$this->episode->id,
$episodePersonId
$episodePersonId,
);
return redirect()->back();
......
......@@ -24,7 +24,7 @@ class PodcastPerson extends BaseController
if (count($params) > 0) {
if (
!($this->podcast = (new PodcastModel())->getPodcastById(
$params[0]
$params[0],
))
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
......@@ -43,8 +43,8 @@ class PodcastPerson extends BaseController
$data = [
'podcast' => $this->podcast,
'podcastPersons' => (new PodcastPersonModel())->getPersonsByPodcastId(
$this->podcast->id
'podcastPersons' => (new PodcastPersonModel())->getPodcastPersons(
$this->podcast->id,
),
'personOptions' => (new PersonModel())->getPersonOptions(),
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
......@@ -71,7 +71,7 @@ class PodcastPerson extends BaseController
(new PodcastPersonModel())->addPodcastPersons(
$this->podcast->id,
$this->request->getPost('person'),
$this->request->getPost('person_group_role')
$this->request->getPost('person_group_role'),
);
return redirect()->back();
......@@ -81,7 +81,7 @@ class PodcastPerson extends BaseController
{
(new PodcastPersonModel())->removePodcastPersons(
$this->podcast->id,
$podcastPersonId
$podcastPersonId,
);
return redirect()->back();
......
......@@ -47,7 +47,7 @@ class Episode extends BaseController
self::triggerWebpageHit($this->podcast->id);
$locale = service('request')->getLocale();
$cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_{$locale}";
$cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_{$locale}";
if (!($cachedView = cache($cacheName))) {
helper('persons');
......@@ -107,7 +107,7 @@ class Episode extends BaseController
$locale = service('request')->getLocale();
$cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
$cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
if (!($cachedView = cache($cacheName))) {
$theme = EpisodeModel::$themes[$theme];
......
......@@ -26,7 +26,7 @@ class Feed extends Controller
$serviceSlug = '';
try {
$service = \Opawg\UserAgentsPhp\UserAgentsRSS::find(
$_SERVER['HTTP_USER_AGENT']
$_SERVER['HTTP_USER_AGENT'],
);
if ($service) {
$serviceSlug = $service['slug'];
......@@ -37,14 +37,14 @@ class Feed extends Controller
}
$cacheName =
"podcast{$podcast->id}_feed" . ($service ? "_{$serviceSlug}" : '');
"podcast#{$podcast->id}_feed" . ($service ? "_{$serviceSlug}" : '');
if (!($found = cache($cacheName))) {
$found = get_rss_feed($podcast, $serviceSlug);
// The page cache is set to expire after next episode publication or a decade by default so it is deleted manually upon podcast update
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$podcast->id
$podcast->id,
);
cache()->save(
......@@ -52,7 +52,7 @@ class Feed extends Controller
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE
: DECADE,
);
}
return $this->response->setXML($found);
......
......@@ -36,37 +36,42 @@ class Page extends BaseController
public function index()
{
// The page cache is set to a decade so it is deleted manually upon page update
$this->cachePage(DECADE);
$cacheName = "page@{$this->page->slug}";
if (!($found = cache($cacheName))) {
$data = [
'page' => $this->page,
];
$data = [
'page' => $this->page,
];
return view('page', $data);
$found = view('page', $data);
// The page cache is set to a decade so it is deleted manually upon page update
cache()->save($cacheName, $found, DECADE);
}
return $found;
}
public function credits()
{
$locale = service('request')->getLocale();
$model = new PodcastModel();
$allPodcasts = $model->findAll();
$allPodcasts = (new PodcastModel())->findAll();
if (!($found = cache("credits_{$locale}"))) {
$cacheName = "paĝe_credits_{$locale}";
if (!($found = cache($cacheName))) {
$page = new \App\Entities\Page([
'title' => lang('Person.credits', [], $locale),
'slug' => 'credits',
'content' => '',
]);
$creditModel = (new CreditModel())->findAll();
$allCredits = (new CreditModel())->findAll();
// Unlike the carpenter, we make a tree from a table:
$person_group = null;
$person_id = null;
$person_role = null;
$credits = [];
foreach ($creditModel as $credit) {
foreach ($allCredits as $credit) {
if ($person_group !== $credit->person_group) {
$person_group = $credit->person_group;
$person_id = $credit->person_id;
......@@ -200,7 +205,7 @@ class Page extends BaseController
$found = view('credits', $data);
cache()->save("credits_{$locale}", $found, DECADE);
cache()->save($cacheName, $found, DECADE);
}
return $found;
......
......@@ -68,7 +68,7 @@ class Podcast extends BaseController
$seasonQuery = $this->request->getGet('season');
if (!$yearQuery and !$seasonQuery) {
$defaultQuery = (new EpisodeModel())->getDefaultQuery(
$defaultQuery = (new PodcastModel())->getDefaultQuery(
$this->podcast->id,
);
if ($defaultQuery) {
......@@ -84,18 +84,19 @@ class Podcast extends BaseController
'_',
array_filter([
'page',
"podcast{$this->podcast->id}",
"podcast#{$this->podcast->id}",
$yearQuery ? 'year' . $yearQuery : null,
$seasonQuery ? 'season' . $seasonQuery : null,
service('request')->getLocale(),
can_user_interact() ? '_interact' : '',
]),
);
if (!($found = cache($cacheName))) {
// Build navigation array
$episodeModel = new EpisodeModel();
$years = $episodeModel->getYears($this->podcast->id);
$seasons = $episodeModel->getSeasons($this->podcast->id);
$podcastModel = new PodcastModel();
$years = $podcastModel->getYears($this->podcast->id);
$seasons = $podcastModel->getSeasons($this->podcast->id);
$episodesNavigation = [];
$activeQuery = null;
......@@ -155,7 +156,7 @@ class Podcast extends BaseController
'podcast' => $this->podcast,
'episodesNav' => $episodesNavigation,
'activeQuery' => $activeQuery,
'episodes' => $episodeModel->getPodcastEpisodes(
'episodes' => (new EpisodeModel())->getPodcastEpisodes(
$this->podcast->id,
$this->podcast->type,
$yearQuery,
......@@ -164,20 +165,20 @@ class Podcast extends BaseController
'persons' => $persons,
];
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
// if user is logged in then send to the authenticated episodes view
if (can_user_interact()) {
return view('podcast/episodes_authenticated', $data, [
$found = view('podcast/episodes_authenticated', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache_name' => $cacheName . '_authenticated',
'cache_name' => $cacheName,
]);
} else {
return view('podcast/episodes', $data, [
$found = view('podcast/episodes', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
......
......@@ -385,7 +385,7 @@ class Episode extends Entity
}
if (empty($this->persons)) {
$this->persons = (new EpisodePersonModel())->getPersonsByEpisodeId(
$this->persons = (new EpisodePersonModel())->getEpisodePersons(
$this->podcast_id,
$this->id,
);
......
......@@ -232,7 +232,7 @@ class Podcast extends Entity
}
if (empty($this->persons)) {
$this->persons = (new PodcastPersonModel())->getPersonsByPodcastId(
$this->persons = (new PodcastPersonModel())->getPodcastPersons(
$this->id,
);
}
......
......@@ -39,7 +39,7 @@ return [
'cacheHandlerOptions' => [
'file' => 'File',
'redis' => 'Redis',
'memcached' => 'Memcached',
'predis' => 'Predis',
],
'next' => 'Next',
'submit' => 'Finish install',
......
......@@ -7,8 +7,6 @@
*/
return [
'not_in_protected_slugs' =>
'The {field} field conflicts with one of the gateway routes (admin, auth or install).',
'min_dims' =>
'{field} is either not an image, or it is not wide or tall enough.',
'is_image_squared' =>
......
......@@ -39,7 +39,7 @@ return [
'cacheHandlerOptions' => [
'file' => 'Fichiers',
'redis' => 'Redis',
'memcached' => 'Memcached',
'predis' => 'Predis',
],
'next' => 'Suivant',
'submit' => 'Terminer l’installation',
......
......@@ -7,8 +7,6 @@
*/
return [
'not_in_protected_slugs' =>
'Le champ {field} est en conflit avec une des routes (admin, auth ou install).',
'min_dims' =>
'{field} n’est pas une image ou n’a pas la taille minimale requise.',
'is_image_squared' =>
......
......@@ -34,21 +34,24 @@ class CategoryModel extends Model
public function getCategoryOptions()
{
if (!($options = cache('category_options'))) {
$locale = service('request')->getLocale();
$cacheName = "category_options_{$locale}";
if (!($options = cache($cacheName))) {
$categories = $this->findAll();
$options = array_reduce(
$categories,
function ($result, $category) {
$result[$category->id] = lang(</