refactor(auth): replace myth/auth with codeigniter/shield + define new roles
closes #222
This commit is contained in:
parent
c760acc79d
commit
c1287cbe6c
|
@ -29,6 +29,7 @@
|
||||||
"bmewburn.vscode-intelephense-client",
|
"bmewburn.vscode-intelephense-client",
|
||||||
"bradlc.vscode-tailwindcss",
|
"bradlc.vscode-tailwindcss",
|
||||||
"breezelin.phpstan",
|
"breezelin.phpstan",
|
||||||
|
"DavidAnson.vscode-markdownlint",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"eamodio.gitlens",
|
"eamodio.gitlens",
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
"runem.lit-plugin",
|
"runem.lit-plugin",
|
||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"stylelint.vscode-stylelint",
|
"stylelint.vscode-stylelint",
|
||||||
"wayou.vscode-todo-highlight"
|
"wayou.vscode-todo-highlight",
|
||||||
|
"yzhang.markdown-all-in-one"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,10 @@ if (! function_exists('view')) {
|
||||||
*/
|
*/
|
||||||
function view(string $name, array $data = [], array $options = []): string
|
function view(string $name, array $data = [], array $options = []): string
|
||||||
{
|
{
|
||||||
|
if (array_key_exists('theme', $options)) {
|
||||||
|
Theme::setTheme($options['theme']);
|
||||||
|
}
|
||||||
|
|
||||||
$path = Theme::path();
|
$path = Theme::path();
|
||||||
|
|
||||||
/** @var CodeIgniter\View\View $renderer */
|
/** @var CodeIgniter\View\View $renderer */
|
||||||
|
@ -55,6 +59,8 @@ if (! function_exists('lang')) {
|
||||||
*
|
*
|
||||||
* @param array<int|string, string> $args
|
* @param array<int|string, string> $args
|
||||||
*
|
*
|
||||||
|
* TODO: remove, and escape args when necessary
|
||||||
|
*
|
||||||
* @return string|string[]
|
* @return string|string[]
|
||||||
*/
|
*/
|
||||||
function lang(string $line, array $args = [], ?string $locale = null, bool $escape = true): string | array
|
function lang(string $line, array $args = [], ?string $locale = null, bool $escape = true): string | array
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Email extends BaseConfig
|
||||||
/**
|
/**
|
||||||
* Type of mail, either 'text' or 'html'
|
* Type of mail, either 'text' or 'html'
|
||||||
*/
|
*/
|
||||||
public string $mailType = 'text';
|
public string $mailType = 'html';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Character set (utf-8, iso-8859-1, etc.)
|
* Character set (utf-8, iso-8859-1, etc.)
|
||||||
|
|
|
@ -9,7 +9,6 @@ use App\Entities\Post;
|
||||||
use App\Models\EpisodeModel;
|
use App\Models\EpisodeModel;
|
||||||
use CodeIgniter\Events\Events;
|
use CodeIgniter\Events\Events;
|
||||||
use CodeIgniter\Exceptions\FrameworkException;
|
use CodeIgniter\Exceptions\FrameworkException;
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
|
@ -56,21 +55,6 @@ Events::on('pre_system', static function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('login', static function (User $user): void {
|
|
||||||
helper('auth');
|
|
||||||
// set interact_as_actor_id value
|
|
||||||
$userPodcasts = $user->podcasts;
|
|
||||||
if ($userPodcasts = $user->podcasts) {
|
|
||||||
set_interact_as_actor($userPodcasts[0]->actor_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Events::on('logout', static function (User $user): void {
|
|
||||||
helper('auth');
|
|
||||||
// remove user's interact_as_actor session
|
|
||||||
remove_interact_as_actor();
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* Fediverse events
|
* Fediverse events
|
||||||
|
|
|
@ -15,8 +15,6 @@ use Modules\Auth\Filters\PermissionFilter;
|
||||||
use Modules\Fediverse\Filters\AllowCorsFilter;
|
use Modules\Fediverse\Filters\AllowCorsFilter;
|
||||||
use Modules\Fediverse\Filters\FediverseFilter;
|
use Modules\Fediverse\Filters\FediverseFilter;
|
||||||
use Modules\PremiumPodcasts\Filters\PodcastUnlockFilter;
|
use Modules\PremiumPodcasts\Filters\PodcastUnlockFilter;
|
||||||
use Myth\Auth\Filters\LoginFilter;
|
|
||||||
use Myth\Auth\Filters\RoleFilter;
|
|
||||||
|
|
||||||
class Filters extends BaseConfig
|
class Filters extends BaseConfig
|
||||||
{
|
{
|
||||||
|
@ -31,8 +29,6 @@ class Filters extends BaseConfig
|
||||||
'honeypot' => Honeypot::class,
|
'honeypot' => Honeypot::class,
|
||||||
'invalidchars' => InvalidChars::class,
|
'invalidchars' => InvalidChars::class,
|
||||||
'secureheaders' => SecureHeaders::class,
|
'secureheaders' => SecureHeaders::class,
|
||||||
'login' => LoginFilter::class,
|
|
||||||
'role' => RoleFilter::class,
|
|
||||||
'permission' => PermissionFilter::class,
|
'permission' => PermissionFilter::class,
|
||||||
'fediverse' => FediverseFilter::class,
|
'fediverse' => FediverseFilter::class,
|
||||||
'allow-cors' => AllowCorsFilter::class,
|
'allow-cors' => AllowCorsFilter::class,
|
||||||
|
@ -86,7 +82,7 @@ class Filters extends BaseConfig
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->filters = [
|
$this->filters = [
|
||||||
'login' => [
|
'session' => [
|
||||||
'before' => [config('Admin')->gateway . '*', config('Analytics')->gateway . '*'],
|
'before' => [config('Admin')->gateway . '*', config('Analytics')->gateway . '*'],
|
||||||
],
|
],
|
||||||
'podcast-unlock' => [
|
'podcast-unlock' => [
|
||||||
|
|
|
@ -214,7 +214,7 @@ $routes->get('/pages/(:slug)', 'PageController/$1', [
|
||||||
$routes->group('@(:podcastHandle)', static function ($routes): void {
|
$routes->group('@(:podcastHandle)', static function ($routes): void {
|
||||||
$routes->post('posts/new', 'PostController::attemptCreate/$1', [
|
$routes->post('posts/new', 'PostController::attemptCreate/$1', [
|
||||||
'as' => 'post-attempt-create',
|
'as' => 'post-attempt-create',
|
||||||
'filter' => 'permission:podcast-manage_publications',
|
'filter' => 'permission:podcast#.manage-publications',
|
||||||
]);
|
]);
|
||||||
// Post
|
// Post
|
||||||
$routes->group('posts/(:uuid)', static function ($routes): void {
|
$routes->group('posts/(:uuid)', static function ($routes): void {
|
||||||
|
@ -251,14 +251,14 @@ $routes->group('@(:podcastHandle)', static function ($routes): void {
|
||||||
// Actions
|
// Actions
|
||||||
$routes->post('action', 'PostController::attemptAction/$1/$2', [
|
$routes->post('action', 'PostController::attemptAction/$1/$2', [
|
||||||
'as' => 'post-attempt-action',
|
'as' => 'post-attempt-action',
|
||||||
'filter' => 'permission:podcast-interact_as',
|
'filter' => 'permission:podcast#.interact-as',
|
||||||
]);
|
]);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'block-actor',
|
'block-actor',
|
||||||
'PostController::attemptBlockActor/$1/$2',
|
'PostController::attemptBlockActor/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'post-attempt-block-actor',
|
'as' => 'post-attempt-block-actor',
|
||||||
'filter' => 'permission:fediverse-block_actors',
|
'filter' => 'permission:fediverse.manage-blocks',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -266,12 +266,12 @@ $routes->group('@(:podcastHandle)', static function ($routes): void {
|
||||||
'PostController::attemptBlockDomain/$1/$2',
|
'PostController::attemptBlockDomain/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'post-attempt-block-domain',
|
'as' => 'post-attempt-block-domain',
|
||||||
'filter' => 'permission:fediverse-block_domains',
|
'filter' => 'permission:fediverse.manage-blocks',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post('delete', 'PostController::attemptDelete/$1/$2', [
|
$routes->post('delete', 'PostController::attemptDelete/$1/$2', [
|
||||||
'as' => 'post-attempt-delete',
|
'as' => 'post-attempt-delete',
|
||||||
'filter' => 'permission:podcast-manage_publications',
|
'filter' => 'permission:podcast#.manage-publications',
|
||||||
]);
|
]);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'remote/(:postAction)',
|
'remote/(:postAction)',
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Security extends BaseConfig
|
||||||
*
|
*
|
||||||
* @var 'cookie'|'session'
|
* @var 'cookie'|'session'
|
||||||
*/
|
*/
|
||||||
public string $csrfProtection = 'cookie';
|
public string $csrfProtection = 'session';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------------
|
||||||
|
|
|
@ -11,7 +11,6 @@ use CodeIgniter\Validation\CreditCardRules;
|
||||||
use CodeIgniter\Validation\FileRules;
|
use CodeIgniter\Validation\FileRules;
|
||||||
use CodeIgniter\Validation\FormatRules;
|
use CodeIgniter\Validation\FormatRules;
|
||||||
use CodeIgniter\Validation\Rules;
|
use CodeIgniter\Validation\Rules;
|
||||||
use Myth\Auth\Authentication\Passwords\ValidationRules as PasswordRules;
|
|
||||||
|
|
||||||
class Validation extends BaseConfig
|
class Validation extends BaseConfig
|
||||||
{
|
{
|
||||||
|
@ -27,7 +26,6 @@ class Validation extends BaseConfig
|
||||||
CreditCardRules::class,
|
CreditCardRules::class,
|
||||||
AppRules::class,
|
AppRules::class,
|
||||||
AppFileRules::class,
|
AppFileRules::class,
|
||||||
PasswordRules::class,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,12 +20,12 @@ class ActorController extends FediverseActorController
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected $helpers = ['auth', 'svg', 'components', 'misc', 'seo'];
|
protected $helpers = ['svg', 'components', 'misc', 'seo'];
|
||||||
|
|
||||||
public function follow(): string
|
public function follow(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
// @phpstan-ignore-next-line
|
// @phpstan-ignore-next-line
|
||||||
$this->registerPodcastWebpageHit($this->actor->podcast->id);
|
$this->registerPodcastWebpageHit($this->actor->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,5 +34,7 @@ abstract class BaseController extends Controller
|
||||||
parent::initController($request, $response, $logger);
|
parent::initController($request, $response, $logger);
|
||||||
|
|
||||||
Theme::setTheme('app');
|
Theme::setTheme('app');
|
||||||
|
|
||||||
|
$this->helpers = array_merge($this->helpers, ['setting']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class CreditsController extends BaseController
|
||||||
|
|
||||||
$cacheName = implode(
|
$cacheName = implode(
|
||||||
'_',
|
'_',
|
||||||
array_filter(['page', 'credits', $locale, can_user_interact() ? 'authenticated' : null]),
|
array_filter(['page', 'credits', $locale, auth()->loggedIn() ? 'authenticated' : null]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! ($found = cache($cacheName))) {
|
if (! ($found = cache($cacheName))) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class EpisodeCommentController extends BaseController
|
||||||
public function view(): string
|
public function view(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,8 @@ class EpisodeCommentController extends BaseController
|
||||||
"comment#{$this->comment->id}",
|
"comment#{$this->comment->id}",
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ class EpisodeCommentController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
// if user is logged in then send to the authenticated activity view
|
// if user is logged in then send to the authenticated activity view
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
return view('episode/comment', $data);
|
return view('episode/comment', $data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ class EpisodeController extends BaseController
|
||||||
public function index(): string
|
public function index(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@ class EpisodeController extends BaseController
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ class EpisodeController extends BaseController
|
||||||
$this->podcast->id,
|
$this->podcast->id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
return view('episode/comments', $data);
|
return view('episode/comments', $data);
|
||||||
|
@ -115,7 +116,7 @@ class EpisodeController extends BaseController
|
||||||
public function activity(): string
|
public function activity(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +130,8 @@ class EpisodeController extends BaseController
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -144,7 +146,7 @@ class EpisodeController extends BaseController
|
||||||
$this->podcast->id,
|
$this->podcast->id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
return view('episode/activity', $data);
|
return view('episode/activity', $data);
|
||||||
|
@ -167,7 +169,7 @@ class EpisodeController extends BaseController
|
||||||
header('Content-Security-Policy: frame-ancestors http://*:* https://*:*');
|
header('Content-Security-Policy: frame-ancestors http://*:* https://*:*');
|
||||||
|
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,20 @@ namespace App\Controllers;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
use Config\Services;
|
use Config\Services;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
class HomeController extends BaseController
|
class HomeController extends BaseController
|
||||||
{
|
{
|
||||||
public function index(): RedirectResponse | string
|
public function index(): RedirectResponse | string
|
||||||
{
|
{
|
||||||
$db = db_connect();
|
$sortOptions = ['activity', 'created_desc', 'created_asc'];
|
||||||
if ($db->getDatabase() === '' || ! $db->tableExists('podcasts')) {
|
$sortBy = in_array($this->request->getGet('sort'), $sortOptions, true) ? $this->request->getGet(
|
||||||
|
'sort'
|
||||||
|
) : 'activity';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$allPodcasts = (new PodcastModel())->getAllPodcasts($sortBy);
|
||||||
|
} catch (Exception) {
|
||||||
// Database connection has not been set or could not find the podcasts table
|
// Database connection has not been set or could not find the podcasts table
|
||||||
// Redirecting to install page because it is likely that Castopod has not been installed yet.
|
// Redirecting to install page because it is likely that Castopod has not been installed yet.
|
||||||
// NB: as base_url wouldn't have been defined here, redirect to install wizard manually
|
// NB: as base_url wouldn't have been defined here, redirect to install wizard manually
|
||||||
|
@ -27,13 +34,6 @@ class HomeController extends BaseController
|
||||||
return redirect()->to(rtrim(host_url(), '/') . $route);
|
return redirect()->to(rtrim(host_url(), '/') . $route);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sortOptions = ['activity', 'created_desc', 'created_asc'];
|
|
||||||
$sortBy = in_array($this->request->getGet('sort'), $sortOptions, true) ? $this->request->getGet(
|
|
||||||
'sort'
|
|
||||||
) : 'activity';
|
|
||||||
|
|
||||||
$allPodcasts = (new PodcastModel())->getAllPodcasts($sortBy);
|
|
||||||
|
|
||||||
// check if there's only one podcast to redirect user to it
|
// check if there's only one podcast to redirect user to it
|
||||||
if (count($allPodcasts) === 1) {
|
if (count($allPodcasts) === 1) {
|
||||||
return redirect()->route('podcast-activity', [$allPodcasts[0]->handle]);
|
return redirect()->route('podcast-activity', [$allPodcasts[0]->handle]);
|
||||||
|
|
|
@ -24,7 +24,8 @@ class MapController extends BaseController
|
||||||
'map',
|
'map',
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ class PageController extends BaseController
|
||||||
$this->page->slug,
|
$this->page->slug,
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class PodcastController extends BaseController
|
||||||
public function activity(): string
|
public function activity(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,8 @@ class PodcastController extends BaseController
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
// if user is logged in then send to the authenticated activity view
|
// if user is logged in then send to the authenticated activity view
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
return view('podcast/activity', $data);
|
return view('podcast/activity', $data);
|
||||||
|
@ -111,7 +112,7 @@ class PodcastController extends BaseController
|
||||||
public function about(): string
|
public function about(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +125,8 @@ class PodcastController extends BaseController
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -138,7 +140,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
// // if user is logged in then send to the authenticated activity view
|
// // if user is logged in then send to the authenticated activity view
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
return view('podcast/about', $data);
|
return view('podcast/about', $data);
|
||||||
|
@ -162,7 +164,7 @@ class PodcastController extends BaseController
|
||||||
public function episodes(): string
|
public function episodes(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +193,8 @@ class PodcastController extends BaseController
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -264,7 +267,7 @@ class PodcastController extends BaseController
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
return view('podcast/episodes', $data);
|
return view('podcast/episodes', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ class PostController extends FediversePostController
|
||||||
public function view(): string
|
public function view(): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,8 @@ class PostController extends FediversePostController
|
||||||
"post#{$this->post->id}",
|
"post#{$this->post->id}",
|
||||||
service('request')
|
service('request')
|
||||||
->getLocale(),
|
->getLocale(),
|
||||||
can_user_interact() ? 'authenticated' : null,
|
auth()
|
||||||
|
->loggedIn() ? 'authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ class PostController extends FediversePostController
|
||||||
];
|
];
|
||||||
|
|
||||||
// if user is logged in then send to the authenticated activity view
|
// if user is logged in then send to the authenticated activity view
|
||||||
if (can_user_interact()) {
|
if (auth()->loggedIn()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
return view('post/post', $data);
|
return view('post/post', $data);
|
||||||
}
|
}
|
||||||
|
@ -239,7 +240,7 @@ class PostController extends FediversePostController
|
||||||
public function remoteAction(string $action): string
|
public function remoteAction(string $action): string
|
||||||
{
|
{
|
||||||
// Prevent analytics hit when authenticated
|
// Prevent analytics hit when authenticated
|
||||||
if (! can_user_interact()) {
|
if (! auth()->loggedIn()) {
|
||||||
$this->registerPodcastWebpageHit($this->podcast->id);
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,12 @@ class AddMedia extends Migration
|
||||||
],
|
],
|
||||||
'uploaded_by' => [
|
'uploaded_by' => [
|
||||||
'type' => 'INT',
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
'unsigned' => true,
|
'unsigned' => true,
|
||||||
],
|
],
|
||||||
'updated_by' => [
|
'updated_by' => [
|
||||||
'type' => 'INT',
|
'type' => 'INT',
|
||||||
|
'constraint' => 11,
|
||||||
'unsigned' => true,
|
'unsigned' => true,
|
||||||
],
|
],
|
||||||
'uploaded_at' => [
|
'uploaded_at' => [
|
|
@ -18,7 +18,6 @@ class AppSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$this->call('AuthSeeder');
|
|
||||||
$this->call('CategorySeeder');
|
$this->call('CategorySeeder');
|
||||||
$this->call('LanguageSeeder');
|
$this->call('LanguageSeeder');
|
||||||
$this->call('PlatformSeeder');
|
$this->call('PlatformSeeder');
|
||||||
|
|
|
@ -1,328 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PermissionSeeder Inserts permissions
|
|
||||||
*
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Database\Seeds;
|
|
||||||
|
|
||||||
use CodeIgniter\Database\Seeder;
|
|
||||||
|
|
||||||
class AuthSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array<string, string>[]
|
|
||||||
*/
|
|
||||||
protected array $groups = [
|
|
||||||
[
|
|
||||||
'name' => 'superadmin',
|
|
||||||
'description' =>
|
|
||||||
'Somebody who has access to all the castopod instance features',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'podcast_admin',
|
|
||||||
'description' =>
|
|
||||||
'Somebody who has access to all the features within a given podcast',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build permissions array as a list of:
|
|
||||||
*
|
|
||||||
* ``` context => [ [action, description], [action, description], ... ] ```
|
|
||||||
*
|
|
||||||
* @var array<string, array<string, string|string[]>[]>
|
|
||||||
*/
|
|
||||||
protected array $permissions = [
|
|
||||||
'settings' => [
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View settings options',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage',
|
|
||||||
'description' => 'Update general settings',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'users' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Create a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all users',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any user info',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_authorizations',
|
|
||||||
'description' => 'Add or remove roles/permissions to a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_bans',
|
|
||||||
'description' => 'Ban / unban a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'force_pass_reset',
|
|
||||||
'description' =>
|
|
||||||
'Force a user to update his password upon next login',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete user without removing him from database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete_permanently',
|
|
||||||
'description' =>
|
|
||||||
'Delete all occurrences of a user from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'pages' => [
|
|
||||||
[
|
|
||||||
'name' => 'manage',
|
|
||||||
'description' => 'List / create / edit / delete pages',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcasts' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add a new podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'import',
|
|
||||||
'description' => 'Import a new podcast from an external feed',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all podcasts and their episodes',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any podcast and their contributors list',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' => 'Delete any podcast from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'episodes' => [
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all episodes of any podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any episode of any podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcast' => [
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_subscriptions',
|
|
||||||
'description' =>
|
|
||||||
'Add / edit / remove podcast subscriptions',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_contributors',
|
|
||||||
'description' =>
|
|
||||||
'Add / remove contributors to a podcast and edit their roles',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_platforms',
|
|
||||||
'description' => 'Set / remove platform links of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_publications',
|
|
||||||
'description' =>
|
|
||||||
'Publish a podcast and publish / unpublish its episodes & posts',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'interact_as',
|
|
||||||
'description' =>
|
|
||||||
'Interact as the podcast to favourite / share or reply to posts.',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcast_episodes' => [
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all episodes of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any episode of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add new episodes for a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit an episode of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete all occurrences of an episode of a podcast from the database',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'person' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add a new person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all persons',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit a person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete permanently any person from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'fediverse' => [
|
|
||||||
[
|
|
||||||
'name' => 'block_actors',
|
|
||||||
'description' =>
|
|
||||||
'Block fediverse actors from interacting with the instance.',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'block_domains',
|
|
||||||
'description' =>
|
|
||||||
'Block fediverse domains from interacting with the instance.',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
$groupId = 0;
|
|
||||||
$dataGroups = [];
|
|
||||||
foreach ($this->groups as $group) {
|
|
||||||
$dataGroups[] = [
|
|
||||||
'id' => ++$groupId,
|
|
||||||
'name' => $group['name'],
|
|
||||||
'description' => $group['description'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map permissions to a format the `auth_permissions` table expects
|
|
||||||
$dataPermissions = [];
|
|
||||||
$dataGroupsPermissions = [];
|
|
||||||
$permissionId = 0;
|
|
||||||
foreach ($this->permissions as $context => $actions) {
|
|
||||||
foreach ($actions as $action) {
|
|
||||||
$dataPermissions[] = [
|
|
||||||
'id' => ++$permissionId,
|
|
||||||
'name' => $context . '-' . $action['name'],
|
|
||||||
'description' => $action['description'],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($action['has_permission'] as $role) {
|
|
||||||
// link permission to specified groups
|
|
||||||
$dataGroupsPermissions[] = [
|
|
||||||
'group_id' => $this->getGroupIdByName($role, $dataGroups),
|
|
||||||
'permission_id' => $permissionId,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->db->table('auth_groups')->countAll() < count($dataPermissions)) {
|
|
||||||
$this->db
|
|
||||||
->table('auth_permissions')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataPermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->db->table('auth_groups')->countAll() < count($dataGroups)) {
|
|
||||||
$this->db
|
|
||||||
->table('auth_groups')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->db->table('auth_groups_permissions')->countAll() < count($dataGroupsPermissions)) {
|
|
||||||
$this->db
|
|
||||||
->table('auth_groups_permissions')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataGroupsPermissions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, string|int>[] $dataGroups
|
|
||||||
*/
|
|
||||||
public static function getGroupIdByName(string $name, array $dataGroups): ?int
|
|
||||||
{
|
|
||||||
foreach ($dataGroups as $group) {
|
|
||||||
if ($group['name'] === $name) {
|
|
||||||
return $group['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,24 +18,32 @@ class TestSeeder extends Seeder
|
||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
helper('setting');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts an active user with the following credentials: username: admin password: AGUehL3P
|
* Inserts an owner with the following credentials: admin: `admin@example.com` password: `AGUehL3P`
|
||||||
*/
|
*/
|
||||||
$this->db->table('users')
|
$this->db->table('users')
|
||||||
->insert([
|
->insert([
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'username' => 'admin',
|
'username' => 'admin',
|
||||||
'email' => 'admin@example.com',
|
'is_owner' => 1,
|
||||||
'password_hash' =>
|
]);
|
||||||
'$2y$10$TXJEHX/djW8jtzgpDVf7dOOCGo5rv1uqtAYWdwwwkttQcDkAeB2.6',
|
|
||||||
'active' => 1,
|
$this->db->table('auth_identities')
|
||||||
|
->insert([
|
||||||
|
'id' => 1,
|
||||||
|
'user_id' => 1,
|
||||||
|
'type' => 'email_password',
|
||||||
|
'secret' => 'admin@example.com',
|
||||||
|
'secret2' => '$2y$10$TXJEHX/djW8jtzgpDVf7dOOCGo5rv1uqtAYWdwwwkttQcDkAeB2.6',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->db
|
$this->db
|
||||||
->table('auth_groups_users')
|
->table('auth_groups_users')
|
||||||
->insert([
|
->insert([
|
||||||
'group_id' => 1,
|
|
||||||
'user_id' => 1,
|
'user_id' => 1,
|
||||||
|
'group' => setting('AuthGroups.mostPowerfulGroup'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ use App\Entities\Podcast;
|
||||||
use App\Models\EpisodeModel;
|
use App\Models\EpisodeModel;
|
||||||
use App\Models\MediaModel;
|
use App\Models\MediaModel;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use App\Models\UserModel;
|
|
||||||
use CodeIgniter\Entity\Entity;
|
use CodeIgniter\Entity\Entity;
|
||||||
use CodeIgniter\Files\File;
|
use CodeIgniter\Files\File;
|
||||||
use CodeIgniter\I18n\Time;
|
use CodeIgniter\I18n\Time;
|
||||||
use Modules\Auth\Entities\User;
|
use CodeIgniter\Shield\Entities\User;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
|
|
|
@ -18,18 +18,18 @@ use App\Models\EpisodeModel;
|
||||||
use App\Models\MediaModel;
|
use App\Models\MediaModel;
|
||||||
use App\Models\PersonModel;
|
use App\Models\PersonModel;
|
||||||
use App\Models\PlatformModel;
|
use App\Models\PlatformModel;
|
||||||
use App\Models\UserModel;
|
|
||||||
use CodeIgniter\Entity\Entity;
|
use CodeIgniter\Entity\Entity;
|
||||||
use CodeIgniter\Files\File;
|
use CodeIgniter\Files\File;
|
||||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||||
use CodeIgniter\I18n\Time;
|
use CodeIgniter\I18n\Time;
|
||||||
|
use CodeIgniter\Shield\Entities\User;
|
||||||
use League\CommonMark\Environment\Environment;
|
use League\CommonMark\Environment\Environment;
|
||||||
use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
use League\CommonMark\Extension\Autolink\AutolinkExtension;
|
||||||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||||||
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
|
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
|
||||||
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
|
use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
|
||||||
use League\CommonMark\MarkdownConverter;
|
use League\CommonMark\MarkdownConverter;
|
||||||
use Modules\Auth\Entities\User;
|
use Modules\Auth\Models\UserModel;
|
||||||
use Modules\PremiumPodcasts\Entities\Subscription;
|
use Modules\PremiumPodcasts\Entities\Subscription;
|
||||||
use Modules\PremiumPodcasts\Models\SubscriptionModel;
|
use Modules\PremiumPodcasts\Models\SubscriptionModel;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
@ -100,6 +100,8 @@ class Podcast extends Entity
|
||||||
{
|
{
|
||||||
protected string $link;
|
protected string $link;
|
||||||
|
|
||||||
|
protected string $at_handle;
|
||||||
|
|
||||||
protected ?Actor $actor = null;
|
protected ?Actor $actor = null;
|
||||||
|
|
||||||
protected ?Image $cover = null;
|
protected ?Image $cover = null;
|
||||||
|
@ -208,6 +210,11 @@ class Podcast extends Entity
|
||||||
'updated_by' => 'integer',
|
'updated_by' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function getAtHandle(): string
|
||||||
|
{
|
||||||
|
return '@' . $this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @noRector ReturnTypeDeclarationRector
|
* @noRector ReturnTypeDeclarationRector
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
use App\Models\ActorModel;
|
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
use Modules\Fediverse\Entities\Actor;
|
|
||||||
|
|
||||||
if (! function_exists('user')) {
|
|
||||||
/**
|
|
||||||
* Returns the User instance for the current logged in user.
|
|
||||||
*/
|
|
||||||
function user(): ?User
|
|
||||||
{
|
|
||||||
$authenticate = service('authentication');
|
|
||||||
$authenticate->check();
|
|
||||||
return $authenticate->user();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! function_exists('set_interact_as_actor')) {
|
|
||||||
/**
|
|
||||||
* Sets the actor id of which the user is acting as
|
|
||||||
*/
|
|
||||||
function set_interact_as_actor(int $actorId): void
|
|
||||||
{
|
|
||||||
$authenticate = service('authentication');
|
|
||||||
$authenticate->check();
|
|
||||||
|
|
||||||
$session = session();
|
|
||||||
$session->set('interact_as_actor_id', $actorId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! function_exists('remove_interact_as_actor')) {
|
|
||||||
/**
|
|
||||||
* Removes the actor id of which the user is acting as
|
|
||||||
*/
|
|
||||||
function remove_interact_as_actor(): void
|
|
||||||
{
|
|
||||||
$session = session();
|
|
||||||
$session->remove('interact_as_actor_id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! function_exists('interact_as_actor_id')) {
|
|
||||||
/**
|
|
||||||
* Sets the podcast id of which the user is acting as
|
|
||||||
*/
|
|
||||||
function interact_as_actor_id(): int
|
|
||||||
{
|
|
||||||
$authenticate = service('authentication');
|
|
||||||
$authenticate->check();
|
|
||||||
|
|
||||||
$session = session();
|
|
||||||
return $session->get('interact_as_actor_id');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! function_exists('interact_as_actor')) {
|
|
||||||
/**
|
|
||||||
* Get the actor the user is currently interacting as
|
|
||||||
*/
|
|
||||||
function interact_as_actor(): Actor | false
|
|
||||||
{
|
|
||||||
$authenticate = service('authentication');
|
|
||||||
$authenticate->check();
|
|
||||||
|
|
||||||
$session = session();
|
|
||||||
if ($session->has('interact_as_actor_id')) {
|
|
||||||
return model(ActorModel::class, false)->getActorById($session->get('interact_as_actor_id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! function_exists('can_user_interact')) {
|
|
||||||
function can_user_interact(): bool
|
|
||||||
{
|
|
||||||
return (bool) interact_as_actor();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@ declare(strict_types=1);
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Entities\Podcast;
|
use App\Entities\Podcast;
|
||||||
use CodeIgniter\Database\Query;
|
|
||||||
use CodeIgniter\HTTP\URI;
|
use CodeIgniter\HTTP\URI;
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
use phpseclib\Crypt\RSA;
|
use phpseclib\Crypt\RSA;
|
||||||
|
@ -205,15 +204,14 @@ class PodcastModel extends Model
|
||||||
/**
|
/**
|
||||||
* Gets all the podcasts a given user is contributing to
|
* Gets all the podcasts a given user is contributing to
|
||||||
*
|
*
|
||||||
|
* @param string[] $userPodcastIds
|
||||||
* @return Podcast[] podcasts
|
* @return Podcast[] podcasts
|
||||||
*/
|
*/
|
||||||
public function getUserPodcasts(int $userId): array
|
public function getUserPodcasts(int $userId, array $userPodcastIds): array
|
||||||
{
|
{
|
||||||
$cacheName = "user{$userId}_podcasts";
|
$cacheName = "user{$userId}_podcasts";
|
||||||
if (! ($found = cache($cacheName))) {
|
if (! ($found = cache($cacheName))) {
|
||||||
$found = $this->select('podcasts.*')
|
$found = $userPodcastIds === [] ? [] : $this->whereIn('id', $userPodcastIds)
|
||||||
->join('podcasts_users', 'podcasts_users.podcast_id = podcasts.id')
|
|
||||||
->where('podcasts_users.user_id', $userId)
|
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
cache()
|
cache()
|
||||||
|
@ -223,76 +221,18 @@ class PodcastModel extends Model
|
||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPodcastContributor(int $userId, int $podcastId, int $groupId): Query | bool
|
public function getContributorGroup(int $userId, int $podcastId): int | false
|
||||||
{
|
{
|
||||||
cache()->delete("podcast#{$podcastId}_contributors");
|
$userPodcast = $this->db
|
||||||
|
->table('auth_groups_users')
|
||||||
$data = [
|
->select('user_id, group')
|
||||||
'user_id' => $userId,
|
->where('user_id', $userId)
|
||||||
'podcast_id' => $podcastId,
|
->like('group', "podcast#{$podcastId}")
|
||||||
'group_id' => $groupId,
|
->get()
|
||||||
];
|
->getResultObject();
|
||||||
|
|
||||||
return $this->db->table('podcasts_users')
|
|
||||||
->insert($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatePodcastContributor(int $userId, int $podcastId, int $groupId): bool
|
|
||||||
{
|
|
||||||
cache()->delete("podcast#{$podcastId}_contributors");
|
|
||||||
|
|
||||||
return $this->db
|
|
||||||
->table('podcasts_users')
|
|
||||||
->where([
|
|
||||||
'user_id' => $userId,
|
|
||||||
'podcast_id' => $podcastId,
|
|
||||||
])
|
|
||||||
->update([
|
|
||||||
'group_id' => $groupId,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removePodcastContributor(int $userId, int $podcastId): string | bool
|
|
||||||
{
|
|
||||||
cache()->delete("podcast#{$podcastId}_contributors");
|
|
||||||
|
|
||||||
return $this->db
|
|
||||||
->table('podcasts_users')
|
|
||||||
->where([
|
|
||||||
'user_id' => $userId,
|
|
||||||
'podcast_id' => $podcastId,
|
|
||||||
])
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContributorGroupId(int $userId, int | string $podcastId): int | false
|
|
||||||
{
|
|
||||||
if (! is_numeric($podcastId)) {
|
|
||||||
// identifier is the podcast name, request must be a join
|
|
||||||
$userPodcast = $this->db
|
|
||||||
->table('podcasts_users')
|
|
||||||
->select('group_id, user_id')
|
|
||||||
->join('podcasts', 'podcasts.id = podcasts_users.podcast_id')
|
|
||||||
->where([
|
|
||||||
'user_id' => $userId,
|
|
||||||
'handle' => $podcastId,
|
|
||||||
])
|
|
||||||
->get()
|
|
||||||
->getResultObject();
|
|
||||||
} else {
|
|
||||||
$userPodcast = $this->db
|
|
||||||
->table('podcasts_users')
|
|
||||||
->select('group_id')
|
|
||||||
->where([
|
|
||||||
'user_id' => $userId,
|
|
||||||
'podcast_id' => $podcastId,
|
|
||||||
])
|
|
||||||
->get()
|
|
||||||
->getResultObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $userPodcast !== []
|
return $userPodcast !== []
|
||||||
? (int) $userPodcast[0]->group_id
|
? (int) $userPodcast[0]->group
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
use Myth\Auth\Models\UserModel as MythAuthUserModel;
|
|
||||||
|
|
||||||
class UserModel extends MythAuthUserModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $returnType = User::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return User[]
|
|
||||||
*/
|
|
||||||
public function getPodcastContributors(int $podcastId): array
|
|
||||||
{
|
|
||||||
$cacheName = "podcast#{$podcastId}_contributors";
|
|
||||||
if (! ($found = cache($cacheName))) {
|
|
||||||
$found = $this->select('users.*, auth_groups.name as podcast_role')
|
|
||||||
->join('podcasts_users', 'podcasts_users.user_id = users.id')
|
|
||||||
->join('auth_groups', 'auth_groups.id = podcasts_users.group_id')
|
|
||||||
->where('podcasts_users.podcast_id', $podcastId)
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
cache()
|
|
||||||
->save($cacheName, $found, DECADE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $found;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPodcastContributor(int $userId, int $podcastId): ?User
|
|
||||||
{
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
return $this->select('users.*, podcasts_users.podcast_id as podcast_id, auth_groups.name as podcast_role')
|
|
||||||
->join('podcasts_users', 'podcasts_users.user_id = users.id')
|
|
||||||
->join('auth_groups', 'auth_groups.id = podcasts_users.group_id')
|
|
||||||
->where([
|
|
||||||
'users.id' => $userId,
|
|
||||||
'podcast_id' => $podcastId,
|
|
||||||
])
|
|
||||||
->first();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
<path d="M3.783 2.826L12 1l8.217 1.826a1 1 0 0 1 .783.976v9.987a6 6 0 0 1-2.672 4.992L12 23l-6.328-4.219A6 6 0 0 1 3 13.79V3.802a1 1 0 0 1 .783-.976zM12 11a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zm-4.473 5h8.946a4.5 4.5 0 0 0-8.946 0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 347 B |
|
@ -0,0 +1,29 @@
|
||||||
|
<?= helper(['components', 'svg']) ?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
|
||||||
|
<title>403 Forbidden</title>
|
||||||
|
<link rel='stylesheet' type='text/css' href='<?= route_to('themes-colors-css') ?>' />
|
||||||
|
<?= service('vite')->asset('styles/index.css', 'css') ?>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="flex flex-col items-center justify-center min-h-screen px-2 text-center bg-base theme-<?= service('settings')
|
||||||
|
->get('App.theme') ?>">
|
||||||
|
<?= svg('castopod-mascot_confused', 'h-64') ?>
|
||||||
|
<h1 class="mt-4 text-3xl font-bold font-display md:text-4xl lg:text-5xl">403 - Forbidden</h1>
|
||||||
|
|
||||||
|
<p class="mb-6 text-lg text-skin-muted md:text-xl lg:text-2xl">
|
||||||
|
<?php if (isset($message) && $message !== '(null)'): ?>
|
||||||
|
<?= esc($message) ?>
|
||||||
|
<?php else: ?>
|
||||||
|
You do not have sufficient permissions to access that page.
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
<a href="<?= previous_url() ?>" class="inline-flex items-center justify-center px-3 py-1 text-sm font-semibold rounded-full shadow-xs text-accent-contrast focus:ring-accent md:px-4 md:py-2 md:text-base bg-accent-base hover:bg-accent-hover"><?= lang('Common.go_back') ?></a>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -14,7 +14,7 @@
|
||||||
<body class="flex flex-col items-center justify-center min-h-screen px-2 text-center bg-base theme-<?= service('settings')
|
<body class="flex flex-col items-center justify-center min-h-screen px-2 text-center bg-base theme-<?= service('settings')
|
||||||
->get('App.theme') ?>">
|
->get('App.theme') ?>">
|
||||||
<?= svg('castopod-mascot_confused', 'h-64') ?>
|
<?= svg('castopod-mascot_confused', 'h-64') ?>
|
||||||
<h1 class="text-3xl font-bold font-display md:text-4xl lg:text-5xl">404 - File Not Found</h1>
|
<h1 class="mt-4 text-3xl font-bold font-display md:text-4xl lg:text-5xl">404 - File Not Found</h1>
|
||||||
|
|
||||||
<p class="mb-6 text-lg text-skin-muted md:text-xl lg:text-2xl">
|
<p class="mb-6 text-lg text-skin-muted md:text-xl lg:text-2xl">
|
||||||
<?php if (isset($message) && $message !== '(null)'): ?>
|
<?php if (isset($message) && $message !== '(null)'): ?>
|
||||||
|
|
|
@ -10,14 +10,14 @@
|
||||||
<title>Whoops!</title>
|
<title>Whoops!</title>
|
||||||
<link rel='stylesheet' type='text/css' href='<?= route_to('themes-colors-css') ?>' />
|
<link rel='stylesheet' type='text/css' href='<?= route_to('themes-colors-css') ?>' />
|
||||||
<?= service('vite')->asset('styles/index.css', 'css') ?>
|
<?= service('vite')->asset('styles/index.css', 'css') ?>
|
||||||
<?php if (service('authentication')->isLoggedIn()): ?>
|
<?php if (auth()->loggedIn()): ?>
|
||||||
<?= service('vite')->asset('js/error.ts', 'js') ?>
|
<?= service('vite')->asset('js/error.ts', 'js') ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="flex flex-col items-center justify-center min-h-screen px-4 bg-base gap-y-12 theme-<?= service('settings')
|
<body class="flex flex-col items-center justify-center min-h-screen px-4 bg-base gap-y-12 theme-<?= service('settings')
|
||||||
->get('App.theme') ?>">
|
->get('App.theme') ?>">
|
||||||
<?php if (service('authentication')->isLoggedIn()): ?>
|
<?php if (auth()->loggedIn()): ?>
|
||||||
<div class="flex flex-col items-center justify-center flex-1 gap-6">
|
<div class="flex flex-col items-center justify-center flex-1 gap-6">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<?= svg('castopod-mascot_confused', 'w-full max-w-xs p-6') ?>
|
<?= svg('castopod-mascot_confused', 'w-full max-w-xs p-6') ?>
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
"james-heinrich/getid3": "^2.0.x-dev",
|
"james-heinrich/getid3": "^2.0.x-dev",
|
||||||
"whichbrowser/parser": "^v2.1.7",
|
"whichbrowser/parser": "^v2.1.7",
|
||||||
"geoip2/geoip2": "v2.13.0",
|
"geoip2/geoip2": "v2.13.0",
|
||||||
"myth/auth": "dev-develop",
|
|
||||||
"league/commonmark": "^2.3.5",
|
"league/commonmark": "^2.3.5",
|
||||||
"vlucas/phpdotenv": "^v5.4.1",
|
"vlucas/phpdotenv": "^v5.4.1",
|
||||||
"league/html-to-markdown": "^v5.1.0",
|
"league/html-to-markdown": "^v5.1.0",
|
||||||
|
@ -23,7 +22,8 @@
|
||||||
"essence/essence": "^3.5.4",
|
"essence/essence": "^3.5.4",
|
||||||
"codeigniter4/settings": "^v2.1.0",
|
"codeigniter4/settings": "^v2.1.0",
|
||||||
"chrisjean/php-ico": "^1.0.4",
|
"chrisjean/php-ico": "^1.0.4",
|
||||||
"melbahja/seo": "^v2.1.1"
|
"melbahja/seo": "^v2.1.1",
|
||||||
|
"codeigniter4/shield": "dev-develop"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"mikey179/vfsstream": "^v1.6.11",
|
"mikey179/vfsstream": "^v1.6.11",
|
||||||
|
|
|
@ -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": "caa3b9ff10584fe03c7be1176713b427",
|
"content-hash": "51482dcb24c719550a1f0aa7e7580dfc",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adaures/ipcat-php",
|
"name": "adaures/ipcat-php",
|
||||||
|
@ -286,6 +286,70 @@
|
||||||
},
|
},
|
||||||
"time": "2021-11-22T17:30:18+00:00"
|
"time": "2021-11-22T17:30:18+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "codeigniter4/shield",
|
||||||
|
"version": "dev-develop",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/codeigniter4/shield.git",
|
||||||
|
"reference": "f4cdfb672b600a032a6f0bfc0b7735411bee0cae"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/codeigniter4/shield/zipball/f4cdfb672b600a032a6f0bfc0b7735411bee0cae",
|
||||||
|
"reference": "f4cdfb672b600a032a6f0bfc0b7735411bee0cae",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"codeigniter4/settings": "^2.0",
|
||||||
|
"php": "^7.4.3 || ^8.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"codeigniter4/authentication-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"codeigniter4/devkit": "^1.0",
|
||||||
|
"codeigniter4/framework": "^4.2.3",
|
||||||
|
"mockery/mockery": "^1.0"
|
||||||
|
},
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Helpers/auth_helper.php",
|
||||||
|
"src/Helpers/email_helper.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"CodeIgniter\\Shield\\": "src"
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": ["**/Database/Migrations/**"]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": ["MIT"],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Lonnie Ezell",
|
||||||
|
"email": "lonnieje@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Authentication and Authorization for CodeIgniter 4",
|
||||||
|
"homepage": "https://github.com/codeigniter4/shield",
|
||||||
|
"keywords": [
|
||||||
|
"Authentication",
|
||||||
|
"authorization",
|
||||||
|
"codeigniter",
|
||||||
|
"codeigniter4"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://github.com/codeigniter4/shield/blob/develop/docs/index.md",
|
||||||
|
"forum": "https://github.com/codeigniter4/shield/discussions",
|
||||||
|
"issues": "https://github.com/codeigniter4/shield/issues",
|
||||||
|
"slack": "https://codeigniterchat.slack.com",
|
||||||
|
"source": "https://github.com/codeigniter4/shield"
|
||||||
|
},
|
||||||
|
"time": "2022-10-05T10:11:44+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/ca-bundle",
|
"name": "composer/ca-bundle",
|
||||||
"version": "1.3.4",
|
"version": "1.3.4",
|
||||||
|
@ -1367,73 +1431,6 @@
|
||||||
},
|
},
|
||||||
"time": "2021-05-10T16:28:01+00:00"
|
"time": "2021-05-10T16:28:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "myth/auth",
|
|
||||||
"version": "dev-develop",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/lonnieezell/myth-auth.git",
|
|
||||||
"reference": "cc94231f5284e9578967aba4796f018809669c84"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/cc94231f5284e9578967aba4796f018809669c84",
|
|
||||||
"reference": "cc94231f5284e9578967aba4796f018809669c84",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.4 || ^8.0"
|
|
||||||
},
|
|
||||||
"provide": {
|
|
||||||
"codeigniter4/authentication-implementation": "1.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"codeigniter4/codeigniter4-standard": "^1.0",
|
|
||||||
"codeigniter4/devkit": "^1.0",
|
|
||||||
"codeigniter4/framework": "^4.1",
|
|
||||||
"mockery/mockery": "^1.0"
|
|
||||||
},
|
|
||||||
"default-branch": true,
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Myth\\Auth\\": "src"
|
|
||||||
},
|
|
||||||
"exclude-from-classmap": ["**/Database/Migrations/**"]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": ["MIT"],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Lonnie Ezell",
|
|
||||||
"email": "lonnieje@gmail.com",
|
|
||||||
"homepage": "http://newmythmedia.com",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Flexible authentication/authorization system for CodeIgniter 4.",
|
|
||||||
"homepage": "https://github.com/lonnieezell/myth-auth",
|
|
||||||
"keywords": ["Authentication", "authorization", "codeigniter"],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/lonnieezell/myth-auth/issues",
|
|
||||||
"source": "https://github.com/lonnieezell/myth-auth/tree/develop"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/lonnieezell",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/mgatner",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/lonnieezell",
|
|
||||||
"type": "patreon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2022-08-01T17:23:52+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "nette/schema",
|
"name": "nette/schema",
|
||||||
"version": "v1.2.2",
|
"version": "v1.2.2",
|
||||||
|
@ -6684,8 +6681,8 @@
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"stability-flags": {
|
"stability-flags": {
|
||||||
"james-heinrich/getid3": 20,
|
"james-heinrich/getid3": 20,
|
||||||
"myth/auth": 20,
|
"michalsn/codeigniter4-uuid": 20,
|
||||||
"michalsn/codeigniter4-uuid": 20
|
"codeigniter4/shield": 20
|
||||||
},
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
|
|
|
@ -22,7 +22,6 @@ build:
|
||||||
script:
|
script:
|
||||||
- npm run build
|
- npm run build
|
||||||
except:
|
except:
|
||||||
- develop
|
|
||||||
- main
|
- main
|
||||||
- beta
|
- beta
|
||||||
- alpha
|
- alpha
|
||||||
|
@ -40,7 +39,6 @@ build-production:
|
||||||
- docs/.vitepress/dist
|
- docs/.vitepress/dist
|
||||||
expire_in: 30 mins
|
expire_in: 30 mins
|
||||||
only:
|
only:
|
||||||
- develop
|
|
||||||
- main
|
- main
|
||||||
- beta
|
- beta
|
||||||
- alpha
|
- alpha
|
||||||
|
@ -72,7 +70,6 @@ deploy:
|
||||||
- rsync -avzuh -e "ssh -p $SSH_PORT" $SOURCE_FOLDER $USER@$HOST:$TEMP_DIRECTORY --progress
|
- rsync -avzuh -e "ssh -p $SSH_PORT" $SOURCE_FOLDER $USER@$HOST:$TEMP_DIRECTORY --progress
|
||||||
- ssh $USER@$HOST -p $SSH_PORT "rsync -rtv $TEMP_DIRECTORY $DIRECTORY"
|
- ssh $USER@$HOST -p $SSH_PORT "rsync -rtv $TEMP_DIRECTORY $DIRECTORY"
|
||||||
only:
|
only:
|
||||||
- develop
|
|
||||||
- main
|
- main
|
||||||
- beta
|
- beta
|
||||||
- alpha
|
- alpha
|
||||||
|
|
|
@ -179,6 +179,7 @@ function getGuideSidebarEn() {
|
||||||
},
|
},
|
||||||
{ text: "Security", link: "/getting-started/security" },
|
{ text: "Security", link: "/getting-started/security" },
|
||||||
{ text: "Update", link: "/getting-started/update" },
|
{ text: "Update", link: "/getting-started/update" },
|
||||||
|
{ text: "Auth", link: "/getting-started/auth" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -207,6 +208,7 @@ function getGuideSidebarFr() {
|
||||||
},
|
},
|
||||||
{ text: "Sécurité", link: "/fr/getting-started/security" },
|
{ text: "Sécurité", link: "/fr/getting-started/security" },
|
||||||
{ text: "Mise à jour", link: "/fr/getting-started/update" },
|
{ text: "Mise à jour", link: "/fr/getting-started/update" },
|
||||||
|
{ text: "Authentification", link: "/fr/getting-started/auth" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -235,6 +237,7 @@ function getGuideSidebarPtBR() {
|
||||||
},
|
},
|
||||||
{ text: "Segurança", link: "/pt-BR/getting-started/security" },
|
{ text: "Segurança", link: "/pt-BR/getting-started/security" },
|
||||||
{ text: "Atualizar", link: "/pt-BR/getting-started/update" },
|
{ text: "Atualizar", link: "/pt-BR/getting-started/update" },
|
||||||
|
{ text: "Autenticação", link: "/pt-BR/getting-started/auth" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -263,6 +266,7 @@ function getGuideSidebarNnNO() {
|
||||||
},
|
},
|
||||||
{ text: "Sikkerhet", link: "/nn-NO/getting-started/security" },
|
{ text: "Sikkerhet", link: "/nn-NO/getting-started/security" },
|
||||||
{ text: "Oppdaterer", link: "/nn-NO/getting-started/update" },
|
{ text: "Oppdaterer", link: "/nn-NO/getting-started/update" },
|
||||||
|
{ text: "Autentisering", link: "/pt-BR/getting-started/auth" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
---
|
||||||
|
title: Authentication & Authorization
|
||||||
|
sidebarDepth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Authentication & Authorization
|
||||||
|
|
||||||
|
Castopod handles authentication and authorization using `codeigniter/shield`
|
||||||
|
coupled with custom rules. Roles and permissions are defined at two levels:
|
||||||
|
|
||||||
|
1. [instance wide](#1-instance-wide-roles-and-permissions)
|
||||||
|
2. [per podcast](#2-per-podcast-roles-and-permissions)
|
||||||
|
|
||||||
|
## 1. Instance wide roles and permissions
|
||||||
|
|
||||||
|
### Instance roles
|
||||||
|
|
||||||
|
<!-- AUTH-INSTANCE-ROLES-LIST:START - Do not remove or modify this section -->
|
||||||
|
|
||||||
|
| role | description | permissions |
|
||||||
|
| ----------- | ----------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| Super admin | Has complete control over Castopod. | admin.\*, podcasts.\*, users.manage, persons.manage, pages.manage, fediverse.manage-blocks |
|
||||||
|
| Manager | Manages Castopod's content. | podcasts.create, podcasts.import, persons.manage, pages.manage |
|
||||||
|
| Podcaster | General users of Castopod. | admin.access |
|
||||||
|
|
||||||
|
<!-- AUTH-INSTANCE-ROLES-LIST:END -->
|
||||||
|
|
||||||
|
### Instance permissions
|
||||||
|
|
||||||
|
<!-- AUTH-INSTANCE-PERMISSIONS-LIST:START - Do not remove or modify this section -->
|
||||||
|
|
||||||
|
| permission | description |
|
||||||
|
| ----------------------- | ------------------------------------------------------------------ |
|
||||||
|
| admin.access | Can access the Castopod admin area. |
|
||||||
|
| admin.settings | Can access the Castopod settings. |
|
||||||
|
| users.manage | Can manage Castopod users. |
|
||||||
|
| persons.manage | Can manage persons. |
|
||||||
|
| pages.manage | Can manage pages. |
|
||||||
|
| podcasts.view | Can view all podcasts. |
|
||||||
|
| podcasts.create | Can create new podcasts. |
|
||||||
|
| podcasts.import | Can import podcasts. |
|
||||||
|
| fediverse.manage-blocks | Can block fediverse actors/domains from interacting with Castopod. |
|
||||||
|
|
||||||
|
<!-- AUTH-INSTANCE-PERMISSIONS-LIST:END -->
|
||||||
|
|
||||||
|
## 2. Per podcast roles and permissions
|
||||||
|
|
||||||
|
### Per podcast roles
|
||||||
|
|
||||||
|
<!-- AUTH-PODCAST-ROLES-LIST:START - Do not remove or modify this section -->
|
||||||
|
|
||||||
|
| role | description | permissions |
|
||||||
|
| ------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Admin | Has complete control of podcast #{id}. | \* |
|
||||||
|
| Editor | Manages content and publications of podcast #{id}. | view, edit, manage-import, manage-persons, manage-platforms, manage-publications, interact-as, episodes.view, episodes.create, episodes.edit, episodes.delete, episodes.manage-persons, episodes.manage-clips, episodes.manage-publications, episodes.manage-comments |
|
||||||
|
| Author | Manages content of podcast #{id} but cannot publish them. | view, manage-persons, episodes.view, episodes.create, episodes.edit, episodes.manage-persons, episodes.manage-clips |
|
||||||
|
| Guest | General contributor of the podcast #{id}. | view, episodes.view |
|
||||||
|
|
||||||
|
<!-- AUTH-PODCAST-ROLES-LIST:END -->
|
||||||
|
|
||||||
|
### Per podcast permissions
|
||||||
|
|
||||||
|
<!-- AUTH-PODCAST-PERMISSIONS-LIST:START - Do not remove or modify this section -->
|
||||||
|
|
||||||
|
| permission | description |
|
||||||
|
| ---------------------------- | ------------------------------------------------------------------------ |
|
||||||
|
| view | Can view dashboard and analytics of podcast #{id}. |
|
||||||
|
| edit | Can edit podcast #{id}. |
|
||||||
|
| delete | Can delete podcast #{id}. |
|
||||||
|
| manage-import | Can synchronize imported podcast #{id}. |
|
||||||
|
| manage-persons | Can manage subscriptions of podcast #{id}. |
|
||||||
|
| manage-subscriptions | Can manage subscriptions of podcast #{id}. |
|
||||||
|
| manage-contributors | Can manage contributors of podcast #{id}. |
|
||||||
|
| manage-platforms | Can set/remove platform links of podcast #{id}. |
|
||||||
|
| manage-publications | Can publish podcast #{id}. |
|
||||||
|
| interact-as | Can interact as the podcast #{id} to favourite, share or reply to posts. |
|
||||||
|
| episodes.view | Can view dashboard and analytics of podcast #{id}. |
|
||||||
|
| episodes.create | Can create episodes for podcast #{id}. |
|
||||||
|
| episodes.edit | Can edit podcast #{id}. |
|
||||||
|
| episodes.delete | Can delete podcast #{id}. |
|
||||||
|
| episodes.manage-persons | Can manage subscriptions of podcast #{id}. |
|
||||||
|
| episodes.manage-clips | Can manage video clips or soundbites of podcast #{id}. |
|
||||||
|
| episodes.manage-publications | Can publish podcast #{id}. |
|
||||||
|
| episodes.manage-comments | Can create/remove episode comments of podcast #{id}. |
|
||||||
|
|
||||||
|
<!-- AUTH-PODCAST-PERMISSIONS-LIST:END -->
|
|
@ -25,60 +25,60 @@ $routes->group(
|
||||||
$routes->group('settings', static function ($routes): void {
|
$routes->group('settings', static function ($routes): void {
|
||||||
$routes->get('/', 'SettingsController', [
|
$routes->get('/', 'SettingsController', [
|
||||||
'as' => 'settings-general',
|
'as' => 'settings-general',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->post('instance', 'SettingsController::attemptInstanceEdit', [
|
$routes->post('instance', 'SettingsController::attemptInstanceEdit', [
|
||||||
'as' => 'settings-instance',
|
'as' => 'settings-instance',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->get('instance-delete-icon', 'SettingsController::deleteIcon', [
|
$routes->get('instance-delete-icon', 'SettingsController::deleteIcon', [
|
||||||
'as' => 'settings-instance-delete-icon',
|
'as' => 'settings-instance-delete-icon',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->post('instance-images-regenerate', 'SettingsController::regenerateImages', [
|
$routes->post('instance-images-regenerate', 'SettingsController::regenerateImages', [
|
||||||
'as' => 'settings-images-regenerate',
|
'as' => 'settings-images-regenerate',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->post('instance-housekeeping-run', 'SettingsController::runHousekeeping', [
|
$routes->post('instance-housekeeping-run', 'SettingsController::runHousekeeping', [
|
||||||
'as' => 'settings-housekeeping-run',
|
'as' => 'settings-housekeeping-run',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->get('theme', 'SettingsController::theme', [
|
$routes->get('theme', 'SettingsController::theme', [
|
||||||
'as' => 'settings-theme',
|
'as' => 'settings-theme',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
$routes->post('theme', 'SettingsController::attemptSetInstanceTheme', [
|
$routes->post('theme', 'SettingsController::attemptSetInstanceTheme', [
|
||||||
'as' => 'settings-theme',
|
'as' => 'settings-theme',
|
||||||
'filter' => 'permission:settings-manage',
|
'filter' => 'permission:admin.settings',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
$routes->group('persons', static function ($routes): void {
|
$routes->group('persons', static function ($routes): void {
|
||||||
$routes->get('/', 'PersonController', [
|
$routes->get('/', 'PersonController', [
|
||||||
'as' => 'person-list',
|
'as' => 'person-list',
|
||||||
'filter' => 'permission:person-list',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->get('new', 'PersonController::create', [
|
$routes->get('new', 'PersonController::create', [
|
||||||
'as' => 'person-create',
|
'as' => 'person-create',
|
||||||
'filter' => 'permission:person-create',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('new', 'PersonController::attemptCreate', [
|
$routes->post('new', 'PersonController::attemptCreate', [
|
||||||
'filter' => 'permission:person-create',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->group('(:num)', static function ($routes): void {
|
$routes->group('(:num)', static function ($routes): void {
|
||||||
$routes->get('/', 'PersonController::view/$1', [
|
$routes->get('/', 'PersonController::view/$1', [
|
||||||
'as' => 'person-view',
|
'as' => 'person-view',
|
||||||
'filter' => 'permission:person-view',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->get('edit', 'PersonController::edit/$1', [
|
$routes->get('edit', 'PersonController::edit/$1', [
|
||||||
'as' => 'person-edit',
|
'as' => 'person-edit',
|
||||||
'filter' => 'permission:person-edit',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('edit', 'PersonController::attemptEdit/$1', [
|
$routes->post('edit', 'PersonController::attemptEdit/$1', [
|
||||||
'filter' => 'permission:person-edit',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
$routes->add('delete', 'PersonController::delete/$1', [
|
$routes->add('delete', 'PersonController::delete/$1', [
|
||||||
'as' => 'person-delete',
|
'as' => 'person-delete',
|
||||||
'filter' => 'permission:person-delete',
|
'filter' => 'permission:persons.manage',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -89,31 +89,31 @@ $routes->group(
|
||||||
]);
|
]);
|
||||||
$routes->get('new', 'PodcastController::create', [
|
$routes->get('new', 'PodcastController::create', [
|
||||||
'as' => 'podcast-create',
|
'as' => 'podcast-create',
|
||||||
'filter' => 'permission:podcasts-create',
|
'filter' => 'permission:podcasts.create',
|
||||||
]);
|
]);
|
||||||
$routes->post('new', 'PodcastController::attemptCreate', [
|
$routes->post('new', 'PodcastController::attemptCreate', [
|
||||||
'filter' => 'permission:podcasts-create',
|
'filter' => 'permission:podcasts.create',
|
||||||
]);
|
]);
|
||||||
$routes->get('import', 'PodcastImportController', [
|
$routes->get('import', 'PodcastImportController', [
|
||||||
'as' => 'podcast-import',
|
'as' => 'podcast-import',
|
||||||
'filter' => 'permission:podcasts-import',
|
'filter' => 'permission:podcasts.import',
|
||||||
]);
|
]);
|
||||||
$routes->post('import', 'PodcastImportController::attemptImport', [
|
$routes->post('import', 'PodcastImportController::attemptImport', [
|
||||||
'filter' => 'permission:podcasts-import',
|
'filter' => 'permission:podcasts.import',
|
||||||
]);
|
]);
|
||||||
// Podcast
|
// Podcast
|
||||||
// Use ids in admin area to help permission and group lookups
|
// Use ids in admin area to help permission and group lookups
|
||||||
$routes->group('(:num)', static function ($routes): void {
|
$routes->group('(:num)', static function ($routes): void {
|
||||||
$routes->get('/', 'PodcastController::view/$1', [
|
$routes->get('/', 'PodcastController::view/$1', [
|
||||||
'as' => 'podcast-view',
|
'as' => 'podcast-view',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
]);
|
]);
|
||||||
$routes->get('edit', 'PodcastController::edit/$1', [
|
$routes->get('edit', 'PodcastController::edit/$1', [
|
||||||
'as' => 'podcast-edit',
|
'as' => 'podcast-edit',
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.edit',
|
||||||
]);
|
]);
|
||||||
$routes->post('edit', 'PodcastController::attemptEdit/$1', [
|
$routes->post('edit', 'PodcastController::attemptEdit/$1', [
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.edit',
|
||||||
]);
|
]);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'publish',
|
'publish',
|
||||||
|
@ -121,7 +121,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'podcast-publish',
|
'as' => 'podcast-publish',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -129,7 +129,7 @@ $routes->group(
|
||||||
'PodcastController::attemptPublish/$1',
|
'PodcastController::attemptPublish/$1',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -138,7 +138,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'podcast-publish_edit',
|
'as' => 'podcast-publish_edit',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -146,7 +146,7 @@ $routes->group(
|
||||||
'PodcastController::attemptPublishEdit/$1',
|
'PodcastController::attemptPublishEdit/$1',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -155,34 +155,34 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'podcast-publish-cancel',
|
'as' => 'podcast-publish-cancel',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get('edit/delete-banner', 'PodcastController::deleteBanner/$1', [
|
$routes->get('edit/delete-banner', 'PodcastController::deleteBanner/$1', [
|
||||||
'as' => 'podcast-banner-delete',
|
'as' => 'podcast-banner-delete',
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.edit',
|
||||||
]);
|
]);
|
||||||
$routes->get('delete', 'PodcastController::delete/$1', [
|
$routes->get('delete', 'PodcastController::delete/$1', [
|
||||||
'as' => 'podcast-delete',
|
'as' => 'podcast-delete',
|
||||||
'filter' => 'permission:podcasts-delete',
|
'filter' => 'permission:podcast#.delete',
|
||||||
]);
|
]);
|
||||||
$routes->post('delete', 'PodcastController::attemptDelete/$1', [
|
$routes->post('delete', 'PodcastController::attemptDelete/$1', [
|
||||||
'filter' => 'permission:podcasts-delete',
|
'filter' => 'permission:podcast#.delete',
|
||||||
]);
|
]);
|
||||||
$routes->get('update', 'PodcastImportController::updateImport/$1', [
|
$routes->get('update', 'PodcastImportController::updateImport/$1', [
|
||||||
'as' => 'podcast-update-feed',
|
'as' => 'podcast-update-feed',
|
||||||
'filter' => 'permission:podcasts-import',
|
'filter' => 'permission:podcast#.manage-import',
|
||||||
]);
|
]);
|
||||||
$routes->group('persons', static function ($routes): void {
|
$routes->group('persons', static function ($routes): void {
|
||||||
$routes->get('/', 'PodcastPersonController/$1', [
|
$routes->get('/', 'PodcastPersonController/$1', [
|
||||||
'as' => 'podcast-persons-manage',
|
'as' => 'podcast-persons-manage',
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.manage-persons',
|
||||||
]);
|
]);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'/',
|
'/',
|
||||||
'PodcastPersonController::attemptAdd/$1',
|
'PodcastPersonController::attemptAdd/$1',
|
||||||
[
|
[
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.manage-persons',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -190,21 +190,21 @@ $routes->group(
|
||||||
'PodcastPersonController::remove/$1/$2',
|
'PodcastPersonController::remove/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-person-remove',
|
'as' => 'podcast-person-remove',
|
||||||
'filter' => 'permission:podcast-edit',
|
'filter' => 'permission:podcast#.manage-persons',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
$routes->group('analytics', static function ($routes): void {
|
$routes->group('analytics', static function ($routes): void {
|
||||||
$routes->get('/', 'PodcastController::viewAnalytics/$1', [
|
$routes->get('/', 'PodcastController::viewAnalytics/$1', [
|
||||||
'as' => 'podcast-analytics',
|
'as' => 'podcast-analytics',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
]);
|
]);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'webpages',
|
'webpages',
|
||||||
'PodcastController::viewAnalyticsWebpages/$1',
|
'PodcastController::viewAnalyticsWebpages/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-webpages',
|
'as' => 'podcast-analytics-webpages',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -212,7 +212,7 @@ $routes->group(
|
||||||
'PodcastController::viewAnalyticsLocations/$1',
|
'PodcastController::viewAnalyticsLocations/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-locations',
|
'as' => 'podcast-analytics-locations',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -220,7 +220,7 @@ $routes->group(
|
||||||
'PodcastController::viewAnalyticsUniqueListeners/$1',
|
'PodcastController::viewAnalyticsUniqueListeners/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-unique-listeners',
|
'as' => 'podcast-analytics-unique-listeners',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -228,7 +228,7 @@ $routes->group(
|
||||||
'PodcastController::viewAnalyticsListeningTime/$1',
|
'PodcastController::viewAnalyticsListeningTime/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-listening-time',
|
'as' => 'podcast-analytics-listening-time',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -236,7 +236,7 @@ $routes->group(
|
||||||
'PodcastController::viewAnalyticsTimePeriods/$1',
|
'PodcastController::viewAnalyticsTimePeriods/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-time-periods',
|
'as' => 'podcast-analytics-time-periods',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -244,7 +244,7 @@ $routes->group(
|
||||||
'PodcastController::viewAnalyticsPlayers/$1',
|
'PodcastController::viewAnalyticsPlayers/$1',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-analytics-players',
|
'as' => 'podcast-analytics-players',
|
||||||
'filter' => 'permission:podcasts-view,podcast-view',
|
'filter' => 'permission:podcast#.view',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -253,17 +253,17 @@ $routes->group(
|
||||||
$routes->get('/', 'EpisodeController::list/$1', [
|
$routes->get('/', 'EpisodeController::list/$1', [
|
||||||
'as' => 'episode-list',
|
'as' => 'episode-list',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:episodes-list,podcast_episodes-list',
|
'permission:podcast#.episodes.view',
|
||||||
]);
|
]);
|
||||||
$routes->get('new', 'EpisodeController::create/$1', [
|
$routes->get('new', 'EpisodeController::create/$1', [
|
||||||
'as' => 'episode-create',
|
'as' => 'episode-create',
|
||||||
'filter' => 'permission:podcast_episodes-create',
|
'filter' => 'permission:podcast#.episodes.create',
|
||||||
]);
|
]);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'new',
|
'new',
|
||||||
'EpisodeController::attemptCreate/$1',
|
'EpisodeController::attemptCreate/$1',
|
||||||
[
|
[
|
||||||
'filter' => 'permission:podcast_episodes-create',
|
'filter' => 'permission:podcast#.episodes.create',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
// Episode
|
// Episode
|
||||||
|
@ -271,17 +271,17 @@ $routes->group(
|
||||||
$routes->get('/', 'EpisodeController::view/$1/$2', [
|
$routes->get('/', 'EpisodeController::view/$1/$2', [
|
||||||
'as' => 'episode-view',
|
'as' => 'episode-view',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:episodes-view,podcast_episodes-view',
|
'permission:podcast#.episodes.view',
|
||||||
]);
|
]);
|
||||||
$routes->get('edit', 'EpisodeController::edit/$1/$2', [
|
$routes->get('edit', 'EpisodeController::edit/$1/$2', [
|
||||||
'as' => 'episode-edit',
|
'as' => 'episode-edit',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.edit',
|
||||||
]);
|
]);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'edit',
|
'edit',
|
||||||
'EpisodeController::attemptEdit/$1/$2',
|
'EpisodeController::attemptEdit/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.edit',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -290,7 +290,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-publish',
|
'as' => 'episode-publish',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -298,7 +298,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptPublish/$1/$2',
|
'EpisodeController::attemptPublish/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -307,7 +307,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-publish_edit',
|
'as' => 'episode-publish_edit',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -315,7 +315,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptPublishEdit/$1/$2',
|
'EpisodeController::attemptPublishEdit/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -324,7 +324,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-publish-cancel',
|
'as' => 'episode-publish-cancel',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -350,7 +350,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-unpublish',
|
'as' => 'episode-unpublish',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -358,7 +358,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptUnpublish/$1/$2',
|
'EpisodeController::attemptUnpublish/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast-manage_publications',
|
'permission:podcast#.episodes.manage-publications',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -367,7 +367,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-delete',
|
'as' => 'episode-delete',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast_episodes-delete',
|
'permission:podcast#.episodes.delete',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -375,7 +375,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptDelete/$1/$2',
|
'EpisodeController::attemptDelete/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast_episodes-delete',
|
'permission:podcast#.episodes.delete',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -383,7 +383,7 @@ $routes->group(
|
||||||
'EpisodeController::transcriptDelete/$1/$2',
|
'EpisodeController::transcriptDelete/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'transcript-delete',
|
'as' => 'transcript-delete',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.edit',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -391,7 +391,7 @@ $routes->group(
|
||||||
'EpisodeController::chaptersDelete/$1/$2',
|
'EpisodeController::chaptersDelete/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'chapters-delete',
|
'as' => 'chapters-delete',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.edit',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -399,7 +399,7 @@ $routes->group(
|
||||||
'SoundbiteController::list/$1/$2',
|
'SoundbiteController::list/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'soundbites-list',
|
'as' => 'soundbites-list',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -407,7 +407,7 @@ $routes->group(
|
||||||
'SoundbiteController::create/$1/$2',
|
'SoundbiteController::create/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'soundbites-create',
|
'as' => 'soundbites-create',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -415,7 +415,7 @@ $routes->group(
|
||||||
'SoundbiteController::attemptCreate/$1/$2',
|
'SoundbiteController::attemptCreate/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'soundbites-create',
|
'as' => 'soundbites-create',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -423,7 +423,7 @@ $routes->group(
|
||||||
'SoundbiteController::delete/$1/$2/$3',
|
'SoundbiteController::delete/$1/$2/$3',
|
||||||
[
|
[
|
||||||
'as' => 'soundbites-delete',
|
'as' => 'soundbites-delete',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -431,7 +431,7 @@ $routes->group(
|
||||||
'VideoClipsController::list/$1/$2',
|
'VideoClipsController::list/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'video-clips-list',
|
'as' => 'video-clips-list',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -439,7 +439,7 @@ $routes->group(
|
||||||
'VideoClipsController::create/$1/$2',
|
'VideoClipsController::create/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'video-clips-create',
|
'as' => 'video-clips-create',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -447,7 +447,7 @@ $routes->group(
|
||||||
'VideoClipsController::attemptCreate/$1/$2',
|
'VideoClipsController::attemptCreate/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'video-clips-create',
|
'as' => 'video-clips-create',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -455,7 +455,7 @@ $routes->group(
|
||||||
'VideoClipsController::view/$1/$2/$3',
|
'VideoClipsController::view/$1/$2/$3',
|
||||||
[
|
[
|
||||||
'as' => 'video-clip',
|
'as' => 'video-clip',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -463,7 +463,7 @@ $routes->group(
|
||||||
'VideoClipsController::retry/$1/$2/$3',
|
'VideoClipsController::retry/$1/$2/$3',
|
||||||
[
|
[
|
||||||
'as' => 'video-clip-retry',
|
'as' => 'video-clip-retry',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -471,7 +471,7 @@ $routes->group(
|
||||||
'VideoClipsController::delete/$1/$2/$3',
|
'VideoClipsController::delete/$1/$2/$3',
|
||||||
[
|
[
|
||||||
'as' => 'video-clip-delete',
|
'as' => 'video-clip-delete',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-clips',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -479,20 +479,20 @@ $routes->group(
|
||||||
'EpisodeController::embed/$1/$2',
|
'EpisodeController::embed/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'embed-add',
|
'as' => 'embed-add',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.edit',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->group('persons', static function ($routes): void {
|
$routes->group('persons', static function ($routes): void {
|
||||||
$routes->get('/', 'EpisodePersonController/$1/$2', [
|
$routes->get('/', 'EpisodePersonController/$1/$2', [
|
||||||
'as' => 'episode-persons-manage',
|
'as' => 'episode-persons-manage',
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast#.episodes.manage-persons',
|
||||||
]);
|
]);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
'/',
|
'/',
|
||||||
'EpisodePersonController::attemptAdd/$1/$2',
|
'EpisodePersonController::attemptAdd/$1/$2',
|
||||||
[
|
[
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast_episodes-edit',
|
'permission:podcast#.episodes.manage-persons',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -501,7 +501,7 @@ $routes->group(
|
||||||
[
|
[
|
||||||
'as' => 'episode-person-remove',
|
'as' => 'episode-person-remove',
|
||||||
'filter' =>
|
'filter' =>
|
||||||
'permission:podcast_episodes-edit',
|
'permission:podcast#.episodes.manage-persons',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -511,7 +511,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptCommentCreate/$1/$2',
|
'EpisodeController::attemptCommentCreate/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'comment-attempt-create',
|
'as' => 'comment-attempt-create',
|
||||||
'filter' => 'permission:podcast-manage_publications',
|
'filter' => 'permission:podcast#.episodes.manage-comments',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -519,7 +519,7 @@ $routes->group(
|
||||||
'EpisodeController::attemptCommentReply/$1/$2/$3',
|
'EpisodeController::attemptCommentReply/$1/$2/$3',
|
||||||
[
|
[
|
||||||
'as' => 'comment-attempt-reply',
|
'as' => 'comment-attempt-reply',
|
||||||
'filter' => 'permission:podcast-manage_publications',
|
'filter' => 'permission:podcast#.episodes.manage-comments',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -527,73 +527,19 @@ $routes->group(
|
||||||
'EpisodeController::attemptCommentDelete/$1/$2',
|
'EpisodeController::attemptCommentDelete/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'comment-attempt-delete',
|
'as' => 'comment-attempt-delete',
|
||||||
'filter' => 'permission:podcast-manage_publications',
|
'filter' => 'permission:podcast#.episodes.manage-comments',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Podcast contributors
|
|
||||||
$routes->group('contributors', static function ($routes): void {
|
|
||||||
$routes->get('/', 'ContributorController::list/$1', [
|
|
||||||
'as' => 'contributor-list',
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcasts-view,podcast-manage_contributors',
|
|
||||||
]);
|
|
||||||
$routes->get('add', 'ContributorController::add/$1', [
|
|
||||||
'as' => 'contributor-add',
|
|
||||||
'filter' => 'permission:podcast-manage_contributors',
|
|
||||||
]);
|
|
||||||
$routes->post(
|
|
||||||
'add',
|
|
||||||
'ContributorController::attemptAdd/$1',
|
|
||||||
[
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcast-manage_contributors',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// Contributor
|
|
||||||
$routes->group('(:num)', static function ($routes): void {
|
|
||||||
$routes->get('/', 'ContributorController::view/$1/$2', [
|
|
||||||
'as' => 'contributor-view',
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcast-manage_contributors',
|
|
||||||
]);
|
|
||||||
$routes->get(
|
|
||||||
'edit',
|
|
||||||
'ContributorController::edit/$1/$2',
|
|
||||||
[
|
|
||||||
'as' => 'contributor-edit',
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcast-manage_contributors',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
$routes->post(
|
|
||||||
'edit',
|
|
||||||
'ContributorController::attemptEdit/$1/$2',
|
|
||||||
[
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcast-manage_contributors',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
$routes->get(
|
|
||||||
'remove',
|
|
||||||
'ContributorController::remove/$1/$2',
|
|
||||||
[
|
|
||||||
'as' => 'contributor-remove',
|
|
||||||
'filter' =>
|
|
||||||
'permission:podcast-manage_contributors',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$routes->group('platforms', static function ($routes): void {
|
$routes->group('platforms', static function ($routes): void {
|
||||||
$routes->get(
|
$routes->get(
|
||||||
'/',
|
'/',
|
||||||
'PodcastPlatformController::platforms/$1/podcasting',
|
'PodcastPlatformController::platforms/$1/podcasting',
|
||||||
[
|
[
|
||||||
'as' => 'platforms-podcasting',
|
'as' => 'platforms-podcasting',
|
||||||
'filter' => 'permission:podcast-manage_platforms',
|
'filter' => 'permission:podcast#.manage-platforms',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -601,7 +547,7 @@ $routes->group(
|
||||||
'PodcastPlatformController::platforms/$1/social',
|
'PodcastPlatformController::platforms/$1/social',
|
||||||
[
|
[
|
||||||
'as' => 'platforms-social',
|
'as' => 'platforms-social',
|
||||||
'filter' => 'permission:podcast-manage_platforms',
|
'filter' => 'permission:podcast#.manage-platforms',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -609,7 +555,7 @@ $routes->group(
|
||||||
'PodcastPlatformController::platforms/$1/funding',
|
'PodcastPlatformController::platforms/$1/funding',
|
||||||
[
|
[
|
||||||
'as' => 'platforms-funding',
|
'as' => 'platforms-funding',
|
||||||
'filter' => 'permission:podcast-manage_platforms',
|
'filter' => 'permission:podcast#.manage-platforms',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->post(
|
$routes->post(
|
||||||
|
@ -617,7 +563,7 @@ $routes->group(
|
||||||
'PodcastPlatformController::attemptPlatformsUpdate/$1/$2',
|
'PodcastPlatformController::attemptPlatformsUpdate/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'platforms-save',
|
'as' => 'platforms-save',
|
||||||
'filter' => 'permission:podcast-manage_platforms',
|
'filter' => 'permission:podcast#.manage-platforms',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -625,7 +571,7 @@ $routes->group(
|
||||||
'PodcastPlatformController::removePodcastPlatform/$1/$2',
|
'PodcastPlatformController::removePodcastPlatform/$1/$2',
|
||||||
[
|
[
|
||||||
'as' => 'podcast-platform-remove',
|
'as' => 'podcast-platform-remove',
|
||||||
'filter' => 'permission:podcast-manage_platforms',
|
'filter' => 'permission:podcast#.manage-platforms',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -633,12 +579,15 @@ $routes->group(
|
||||||
$routes->group('notifications', static function ($routes): void {
|
$routes->group('notifications', static function ($routes): void {
|
||||||
$routes->get('/', 'NotificationController::list/$1', [
|
$routes->get('/', 'NotificationController::list/$1', [
|
||||||
'as' => 'notification-list',
|
'as' => 'notification-list',
|
||||||
|
'filter' => 'permission:podcast#.view',
|
||||||
]);
|
]);
|
||||||
$routes->get('(:num)/mark-as-read', 'NotificationController::markAsRead/$1/$2', [
|
$routes->get('(:num)/mark-as-read', 'NotificationController::markAsRead/$1/$2', [
|
||||||
'as' => 'notification-mark-as-read',
|
'as' => 'notification-mark-as-read',
|
||||||
|
'filter' => 'permission:podcast#.manage-notifications',
|
||||||
]);
|
]);
|
||||||
$routes->get('mark-all-as-read', 'NotificationController::markAllAsRead/$1', [
|
$routes->get('mark-all-as-read', 'NotificationController::markAllAsRead/$1', [
|
||||||
'as' => 'notification-mark-all-as-read',
|
'as' => 'notification-mark-all-as-read',
|
||||||
|
'filter' => 'permission:podcast#.manage-notifications',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -653,7 +602,7 @@ $routes->group(
|
||||||
'FediverseController::blockedActors',
|
'FediverseController::blockedActors',
|
||||||
[
|
[
|
||||||
'as' => 'fediverse-blocked-actors',
|
'as' => 'fediverse-blocked-actors',
|
||||||
'filter' => 'permission:fediverse-block_actors',
|
'filter' => 'permission:fediverse.manage-blocks',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$routes->get(
|
$routes->get(
|
||||||
|
@ -661,7 +610,7 @@ $routes->group(
|
||||||
'FediverseController::blockedDomains',
|
'FediverseController::blockedDomains',
|
||||||
[
|
[
|
||||||
'as' => 'fediverse-blocked-domains',
|
'as' => 'fediverse-blocked-domains',
|
||||||
'filter' => 'permission:fediverse-block_domains',
|
'filter' => 'permission:fediverse.manage-blocks',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -669,13 +618,14 @@ $routes->group(
|
||||||
$routes->group('pages', static function ($routes): void {
|
$routes->group('pages', static function ($routes): void {
|
||||||
$routes->get('/', 'PageController::list', [
|
$routes->get('/', 'PageController::list', [
|
||||||
'as' => 'page-list',
|
'as' => 'page-list',
|
||||||
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
$routes->get('new', 'PageController::create', [
|
$routes->get('new', 'PageController::create', [
|
||||||
'as' => 'page-create',
|
'as' => 'page-create',
|
||||||
'filter' => 'permission:pages-manage',
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('new', 'PageController::attemptCreate', [
|
$routes->post('new', 'PageController::attemptCreate', [
|
||||||
'filter' => 'permission:pages-manage',
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
$routes->group('(:num)', static function ($routes): void {
|
$routes->group('(:num)', static function ($routes): void {
|
||||||
$routes->get('/', 'PageController::view/$1', [
|
$routes->get('/', 'PageController::view/$1', [
|
||||||
|
@ -683,78 +633,16 @@ $routes->group(
|
||||||
]);
|
]);
|
||||||
$routes->get('edit', 'PageController::edit/$1', [
|
$routes->get('edit', 'PageController::edit/$1', [
|
||||||
'as' => 'page-edit',
|
'as' => 'page-edit',
|
||||||
'filter' => 'permission:pages-manage',
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
$routes->post('edit', 'PageController::attemptEdit/$1', [
|
$routes->post('edit', 'PageController::attemptEdit/$1', [
|
||||||
'filter' => 'permission:pages-manage',
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
$routes->get('delete', 'PageController::delete/$1', [
|
$routes->get('delete', 'PageController::delete/$1', [
|
||||||
'as' => 'page-delete',
|
'as' => 'page-delete',
|
||||||
'filter' => 'permission:pages-manage',
|
'filter' => 'permission:pages.manage',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Users
|
|
||||||
$routes->group('users', static function ($routes): void {
|
|
||||||
$routes->get('/', 'UserController::list', [
|
|
||||||
'as' => 'user-list',
|
|
||||||
'filter' => 'permission:users-list',
|
|
||||||
]);
|
|
||||||
$routes->get('new', 'UserController::create', [
|
|
||||||
'as' => 'user-create',
|
|
||||||
'filter' => 'permission:users-create',
|
|
||||||
]);
|
|
||||||
$routes->post('new', 'UserController::attemptCreate', [
|
|
||||||
'filter' => 'permission:users-create',
|
|
||||||
]);
|
|
||||||
// User
|
|
||||||
$routes->group('(:num)', static function ($routes): void {
|
|
||||||
$routes->get('/', 'UserController::view/$1', [
|
|
||||||
'as' => 'user-view',
|
|
||||||
'filter' => 'permission:users-view',
|
|
||||||
]);
|
|
||||||
$routes->get('edit', 'UserController::edit/$1', [
|
|
||||||
'as' => 'user-edit',
|
|
||||||
'filter' => 'permission:users-manage_authorizations',
|
|
||||||
]);
|
|
||||||
$routes->post('edit', 'UserController::attemptEdit/$1', [
|
|
||||||
'filter' => 'permission:users-manage_authorizations',
|
|
||||||
]);
|
|
||||||
$routes->get('ban', 'UserController::ban/$1', [
|
|
||||||
'as' => 'user-ban',
|
|
||||||
'filter' => 'permission:users-manage_bans',
|
|
||||||
]);
|
|
||||||
$routes->get('unban', 'UserController::unBan/$1', [
|
|
||||||
'as' => 'user-unban',
|
|
||||||
'filter' => 'permission:users-manage_bans',
|
|
||||||
]);
|
|
||||||
$routes->get(
|
|
||||||
'force-pass-reset',
|
|
||||||
'UserController::forcePassReset/$1',
|
|
||||||
[
|
|
||||||
'as' => 'user-force_pass_reset',
|
|
||||||
'filter' => 'permission:users-force_pass_reset',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
$routes->get('delete', 'UserController::delete/$1', [
|
|
||||||
'as' => 'user-delete',
|
|
||||||
'filter' => 'permission:users-delete',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// My account
|
|
||||||
$routes->group('my-account', static function ($routes): void {
|
|
||||||
$routes->get('/', 'MyAccountController', [
|
|
||||||
'as' => 'my-account',
|
|
||||||
]);
|
|
||||||
$routes->get(
|
|
||||||
'change-password',
|
|
||||||
'MyAccountController::changePassword/$1',
|
|
||||||
[
|
|
||||||
'as' => 'change-password',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
$routes->post('change-password', 'MyAccountController::attemptChange/$1');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,203 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Admin\Controllers;
|
|
||||||
|
|
||||||
use App\Entities\Podcast;
|
|
||||||
use App\Models\PodcastModel;
|
|
||||||
use App\Models\UserModel;
|
|
||||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
|
||||||
use Exception;
|
|
||||||
use Modules\Auth\Authorization\GroupModel;
|
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
|
|
||||||
class ContributorController extends BaseController
|
|
||||||
{
|
|
||||||
protected Podcast $podcast;
|
|
||||||
|
|
||||||
protected ?User $user;
|
|
||||||
|
|
||||||
public function _remap(string $method, string ...$params): mixed
|
|
||||||
{
|
|
||||||
if ($params === []) {
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($podcast = (new PodcastModel())->getPodcastById((int) $params[0])) === null) {
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->podcast = $podcast;
|
|
||||||
|
|
||||||
if (count($params) <= 1) {
|
|
||||||
return $this->{$method}();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($this->user = (new UserModel())->getPodcastContributor((int) $params[1], (int) $params[0])) !== null) {
|
|
||||||
return $this->{$method}();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function list(): string
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'podcast' => $this->podcast,
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->podcast->title,
|
|
||||||
]);
|
|
||||||
return view('contributor/list', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view(): string
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'podcast' => $this->podcast,
|
|
||||||
'contributor' => (new UserModel())->getPodcastContributor($this->user->id, $this->podcast->id),
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->podcast->title,
|
|
||||||
1 => $this->user->username,
|
|
||||||
]);
|
|
||||||
return view('contributor/view', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function add(): string
|
|
||||||
{
|
|
||||||
helper('form');
|
|
||||||
|
|
||||||
$users = (new UserModel())->findAll();
|
|
||||||
$userOptions = array_reduce(
|
|
||||||
$users,
|
|
||||||
static function ($result, $user) {
|
|
||||||
$result[$user->id] = $user->username;
|
|
||||||
return $result;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
$roles = (new GroupModel())->getContributorRoles();
|
|
||||||
$roleOptions = array_reduce(
|
|
||||||
$roles,
|
|
||||||
static function ($result, $role) {
|
|
||||||
$result[$role->id] = lang('Contributor.roles.' . $role->name);
|
|
||||||
return $result;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'podcast' => $this->podcast,
|
|
||||||
'userOptions' => $userOptions,
|
|
||||||
'roleOptions' => $roleOptions,
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->podcast->title,
|
|
||||||
]);
|
|
||||||
return view('contributor/add', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attemptAdd(): RedirectResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
(new PodcastModel())->addPodcastContributor(
|
|
||||||
(int) $this->request->getPost('user'),
|
|
||||||
$this->podcast->id,
|
|
||||||
(int) $this->request->getPost('role'),
|
|
||||||
);
|
|
||||||
} catch (Exception) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', [lang('Contributor.messages.alreadyAddedError')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('contributor-list', [$this->podcast->id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(): string
|
|
||||||
{
|
|
||||||
helper('form');
|
|
||||||
|
|
||||||
$roles = (new GroupModel())->getContributorRoles();
|
|
||||||
$roleOptions = array_reduce(
|
|
||||||
$roles,
|
|
||||||
static function ($result, $role) {
|
|
||||||
$result[$role->id] = lang('Contributor.roles.' . $role->name);
|
|
||||||
return $result;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'podcast' => $this->podcast,
|
|
||||||
'user' => $this->user,
|
|
||||||
'contributorGroupId' => (new PodcastModel())->getContributorGroupId(
|
|
||||||
$this->user->id,
|
|
||||||
$this->podcast->id,
|
|
||||||
),
|
|
||||||
'roleOptions' => $roleOptions,
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->podcast->title,
|
|
||||||
1 => $this->user->username,
|
|
||||||
]);
|
|
||||||
return view('contributor/edit', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attemptEdit(): RedirectResponse
|
|
||||||
{
|
|
||||||
(new PodcastModel())->updatePodcastContributor(
|
|
||||||
$this->user->id,
|
|
||||||
$this->podcast->id,
|
|
||||||
(int) $this->request->getPost('role'),
|
|
||||||
);
|
|
||||||
|
|
||||||
return redirect()->route('contributor-edit', [$this->podcast->id, $this->user->id])->with(
|
|
||||||
'message',
|
|
||||||
lang('Contributor.messages.editSuccess')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function remove(): RedirectResponse
|
|
||||||
{
|
|
||||||
if ($this->podcast->created_by === $this->user->id) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', [lang('Contributor.messages.removeOwnerError')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$podcastModel = new PodcastModel();
|
|
||||||
if (
|
|
||||||
! $podcastModel->removePodcastContributor($this->user->id, $this->podcast->id)
|
|
||||||
) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', $podcastModel->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()
|
|
||||||
->route('contributor-list', [$this->podcast->id])
|
|
||||||
->with(
|
|
||||||
'message',
|
|
||||||
lang('Contributor.messages.removeSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
'podcastTitle' => $this->podcast->title,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -95,7 +95,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('episode/list', $data);
|
return view('episode/list', $data);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/view', $data);
|
return view('episode/view', $data);
|
||||||
|
@ -125,7 +125,7 @@ class EpisodeController extends BaseController
|
||||||
'nextEpisodeNumber' => (new EpisodeModel())->getNextEpisodeNumber($this->podcast->id, $currentSeasonNumber),
|
'nextEpisodeNumber' => (new EpisodeModel())->getNextEpisodeNumber($this->podcast->id, $currentSeasonNumber),
|
||||||
];
|
];
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('episode/create', $data);
|
return view('episode/create', $data);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/edit', $data);
|
return view('episode/edit', $data);
|
||||||
|
@ -438,7 +438,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/publish', $data);
|
return view('episode/publish', $data);
|
||||||
|
@ -551,7 +551,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/publish_edit', $data);
|
return view('episode/publish_edit', $data);
|
||||||
|
@ -851,7 +851,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/delete', $data);
|
return view('episode/delete', $data);
|
||||||
|
@ -949,7 +949,7 @@ class EpisodeController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/embed', $data);
|
return view('episode/embed', $data);
|
||||||
|
|
|
@ -59,7 +59,7 @@ class EpisodePersonController extends BaseController
|
||||||
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
||||||
];
|
];
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/persons', $data);
|
return view('episode/persons', $data);
|
||||||
|
|
|
@ -67,7 +67,7 @@ class NotificationController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return view('podcast/notifications', $data);
|
return view('podcast/notifications', $data);
|
||||||
|
|
|
@ -23,7 +23,6 @@ use App\Models\PostModel;
|
||||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
use CodeIgniter\I18n\Time;
|
use CodeIgniter\I18n\Time;
|
||||||
use Config\Services;
|
|
||||||
use Modules\Analytics\Models\AnalyticsPodcastByCountryModel;
|
use Modules\Analytics\Models\AnalyticsPodcastByCountryModel;
|
||||||
use Modules\Analytics\Models\AnalyticsPodcastByEpisodeModel;
|
use Modules\Analytics\Models\AnalyticsPodcastByEpisodeModel;
|
||||||
use Modules\Analytics\Models\AnalyticsPodcastByHourModel;
|
use Modules\Analytics\Models\AnalyticsPodcastByHourModel;
|
||||||
|
@ -56,13 +55,13 @@ class PodcastController extends BaseController
|
||||||
|
|
||||||
public function list(): string
|
public function list(): string
|
||||||
{
|
{
|
||||||
if (! has_permission('podcasts-list')) {
|
if (auth()->user()->can('podcasts.view')) {
|
||||||
$data = [
|
$data = [
|
||||||
'podcasts' => (new PodcastModel())->getUserPodcasts((int) user_id()),
|
'podcasts' => (new PodcastModel())->findAll(),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$data = [
|
$data = [
|
||||||
'podcasts' => (new PodcastModel())->findAll(),
|
'podcasts' => get_user_podcasts(auth()->user()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/view', $data);
|
return view('podcast/view', $data);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +87,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/index', $data);
|
return view('podcast/analytics/index', $data);
|
||||||
}
|
}
|
||||||
|
@ -100,7 +99,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/webpages', $data);
|
return view('podcast/analytics/webpages', $data);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +111,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/locations', $data);
|
return view('podcast/analytics/locations', $data);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +123,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/unique_listeners', $data);
|
return view('podcast/analytics/unique_listeners', $data);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +135,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/listening_time', $data);
|
return view('podcast/analytics/listening_time', $data);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +147,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/time_periods', $data);
|
return view('podcast/analytics/time_periods', $data);
|
||||||
}
|
}
|
||||||
|
@ -160,7 +159,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/analytics/players', $data);
|
return view('podcast/analytics/players', $data);
|
||||||
}
|
}
|
||||||
|
@ -253,10 +252,11 @@ class PodcastController extends BaseController
|
||||||
->with('errors', $podcastModel->errors());
|
->with('errors', $podcastModel->errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
$authorize = Services::authorization();
|
// generate podcast roles and permissions
|
||||||
$podcastAdminGroup = $authorize->group('podcast_admin');
|
// before setting current user as podcast admin
|
||||||
|
config('AuthGroups')
|
||||||
$podcastModel->addPodcastContributor(user_id(), $newPodcastId, (int) $podcastAdminGroup->id);
|
->generatePodcastAuthorizations($newPodcastId);
|
||||||
|
add_podcast_group(auth()->user(), (int) $newPodcastId, setting('AuthGroups.mostPowerfulPodcastGroup'));
|
||||||
|
|
||||||
// set Podcast categories
|
// set Podcast categories
|
||||||
(new CategoryModel())->setPodcastCategories(
|
(new CategoryModel())->setPodcastCategories(
|
||||||
|
@ -264,10 +264,6 @@ class PodcastController extends BaseController
|
||||||
$this->request->getPost('other_categories') ?? [],
|
$this->request->getPost('other_categories') ?? [],
|
||||||
);
|
);
|
||||||
|
|
||||||
// set interact as the newly created podcast actor
|
|
||||||
$createdPodcast = (new PodcastModel())->getPodcastById($newPodcastId);
|
|
||||||
set_interact_as_actor($createdPodcast->actor_id);
|
|
||||||
|
|
||||||
$db->transComplete();
|
$db->transComplete();
|
||||||
|
|
||||||
return redirect()->route('podcast-view', [$newPodcastId])->with(
|
return redirect()->route('podcast-view', [$newPodcastId])->with(
|
||||||
|
@ -290,7 +286,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/edit', $data);
|
return view('podcast/edit', $data);
|
||||||
}
|
}
|
||||||
|
@ -444,7 +440,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/delete', $data);
|
return view('podcast/delete', $data);
|
||||||
}
|
}
|
||||||
|
@ -576,15 +572,6 @@ class PodcastController extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->podcast->actor_id === interact_as_actor_id()) {
|
|
||||||
//set interact to the most recently created podcast actor
|
|
||||||
$mostRecentPodcast = (new PodcastModel())->orderBy('created_at', 'desc')
|
|
||||||
->first();
|
|
||||||
if ($mostRecentPodcast !== null) {
|
|
||||||
set_interact_as_actor($mostRecentPodcast->actor_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$db->transComplete();
|
$db->transComplete();
|
||||||
|
|
||||||
//delete podcast media files and folder
|
//delete podcast media files and folder
|
||||||
|
@ -620,7 +607,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return view('podcast/publish', $data);
|
return view('podcast/publish', $data);
|
||||||
|
@ -754,7 +741,7 @@ class PodcastController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return view('podcast/publish_edit', $data);
|
return view('podcast/publish_edit', $data);
|
||||||
|
|
|
@ -23,7 +23,6 @@ use App\Models\PlatformModel;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
use Config\Services;
|
|
||||||
use ErrorException;
|
use ErrorException;
|
||||||
use League\HTMLToMarkdown\HtmlConverter;
|
use League\HTMLToMarkdown\HtmlConverter;
|
||||||
|
|
||||||
|
@ -201,10 +200,11 @@ class PodcastImportController extends BaseController
|
||||||
->with('errors', $podcastModel->errors());
|
->with('errors', $podcastModel->errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
$authorize = Services::authorization();
|
// set current user as podcast admin
|
||||||
$podcastAdminGroup = $authorize->group('podcast_admin');
|
// 1. create new group
|
||||||
|
config('AuthGroups')
|
||||||
$podcastModel->addPodcastContributor(user_id(), $newPodcastId, (int) $podcastAdminGroup->id);
|
->generatePodcastAuthorizations($newPodcastId);
|
||||||
|
add_podcast_group(auth()->user(), $newPodcastId, 'admin');
|
||||||
|
|
||||||
$podcastsPlatformsData = [];
|
$podcastsPlatformsData = [];
|
||||||
$platformTypes = [
|
$platformTypes = [
|
||||||
|
@ -460,9 +460,7 @@ class PodcastImportController extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set interact as the newly imported podcast actor
|
|
||||||
$importedPodcast = (new PodcastModel())->getPodcastById($newPodcastId);
|
$importedPodcast = (new PodcastModel())->getPodcastById($newPodcastId);
|
||||||
set_interact_as_actor($importedPodcast->actor_id);
|
|
||||||
|
|
||||||
// set podcast publication date
|
// set podcast publication date
|
||||||
$importedPodcast->published_at = $firstEpisodePublicationDate ?? $importedPodcast->created_at;
|
$importedPodcast->published_at = $firstEpisodePublicationDate ?? $importedPodcast->created_at;
|
||||||
|
|
|
@ -47,7 +47,7 @@ class PodcastPersonController extends BaseController
|
||||||
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
|
||||||
];
|
];
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
return view('podcast/persons', $data);
|
return view('podcast/persons', $data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class PodcastPlatformController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return view('podcast/platforms', $data);
|
return view('podcast/platforms', $data);
|
||||||
|
|
|
@ -77,7 +77,7 @@ class SoundbiteController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/soundbites_list', $data);
|
return view('episode/soundbites_list', $data);
|
||||||
|
@ -93,7 +93,7 @@ class SoundbiteController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/soundbites_new', $data);
|
return view('episode/soundbites_new', $data);
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Admin\Controllers;
|
|
||||||
|
|
||||||
use App\Models\UserModel;
|
|
||||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
|
||||||
use Config\Services;
|
|
||||||
use Modules\Auth\Authorization\GroupModel;
|
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
|
|
||||||
class UserController extends BaseController
|
|
||||||
{
|
|
||||||
protected ?User $user;
|
|
||||||
|
|
||||||
public function _remap(string $method, string ...$params): mixed
|
|
||||||
{
|
|
||||||
if ($params === []) {
|
|
||||||
return $this->{$method}();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->user = (new UserModel())->find($params[0])) {
|
|
||||||
return $this->{$method}();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw PageNotFoundException::forPageNotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function list(): string
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'users' => (new UserModel())->findAll(),
|
|
||||||
];
|
|
||||||
|
|
||||||
return view('user/list', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view(): string
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'user' => $this->user,
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->user->username,
|
|
||||||
]);
|
|
||||||
return view('user/view', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(): string
|
|
||||||
{
|
|
||||||
helper('form');
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'roles' => (new GroupModel())->getUserRoles(),
|
|
||||||
];
|
|
||||||
|
|
||||||
return view('user/create', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attemptCreate(): RedirectResponse
|
|
||||||
{
|
|
||||||
$userModel = new UserModel();
|
|
||||||
|
|
||||||
// Validate here first, since some things,
|
|
||||||
// like the password, can only be validated properly here.
|
|
||||||
$rules = array_merge(
|
|
||||||
$userModel->getValidationRules([
|
|
||||||
'only' => ['username'],
|
|
||||||
]),
|
|
||||||
[
|
|
||||||
'email' => 'required|valid_email|is_unique[users.email]',
|
|
||||||
'password' => 'required|strong_password',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', $this->validator->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the user
|
|
||||||
$user = new User($this->request->getPost());
|
|
||||||
|
|
||||||
// Activate user
|
|
||||||
$user->activate();
|
|
||||||
|
|
||||||
// Force user to reset his password on first connection
|
|
||||||
$user->forcePasswordReset();
|
|
||||||
|
|
||||||
if (! $userModel->insert($user)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', $userModel->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success!
|
|
||||||
return redirect()
|
|
||||||
->route('user-list')
|
|
||||||
->with('message', lang('User.messages.createSuccess', [
|
|
||||||
'username' => $user->username,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(): string
|
|
||||||
{
|
|
||||||
helper('form');
|
|
||||||
|
|
||||||
$roles = (new GroupModel())->getUserRoles();
|
|
||||||
$roleOptions = array_reduce(
|
|
||||||
$roles,
|
|
||||||
static function ($result, $role) {
|
|
||||||
$result[$role->name] = lang('User.roles.' . $role->name);
|
|
||||||
return $result;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'user' => $this->user,
|
|
||||||
'roleOptions' => $roleOptions,
|
|
||||||
];
|
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
|
||||||
0 => $this->user->username,
|
|
||||||
]);
|
|
||||||
return view('user/edit', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attemptEdit(): RedirectResponse
|
|
||||||
{
|
|
||||||
$authorize = Services::authorization();
|
|
||||||
|
|
||||||
$roles = $this->request->getPost('roles');
|
|
||||||
|
|
||||||
if ($this->user->isOwner) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', [
|
|
||||||
lang('User.messages.editOwnerError', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$authorize->setUserGroups($this->user->id, $roles ?? []);
|
|
||||||
|
|
||||||
// Success!
|
|
||||||
return redirect()
|
|
||||||
->route('user-list')
|
|
||||||
->with('message', lang('User.messages.rolesEditSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function forcePassReset(): RedirectResponse
|
|
||||||
{
|
|
||||||
$userModel = new UserModel();
|
|
||||||
$this->user->forcePasswordReset();
|
|
||||||
|
|
||||||
if (! $userModel->update($this->user->id, $this->user)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', $userModel->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success!
|
|
||||||
return redirect()
|
|
||||||
->route('user-list')
|
|
||||||
->with(
|
|
||||||
'message',
|
|
||||||
lang('User.messages.forcePassResetSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ban(): RedirectResponse
|
|
||||||
{
|
|
||||||
$authorize = Services::authorization();
|
|
||||||
if ($authorize->inGroup('superadmin', $this->user->id)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', [
|
|
||||||
lang('User.messages.banSuperAdminError', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$userModel = new UserModel();
|
|
||||||
// TODO: add ban reason?
|
|
||||||
$this->user->ban('');
|
|
||||||
|
|
||||||
if (! $userModel->update($this->user->id, $this->user)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', $userModel->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()
|
|
||||||
->route('user-list')
|
|
||||||
->with('message', lang('User.messages.banSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unBan(): RedirectResponse
|
|
||||||
{
|
|
||||||
$userModel = new UserModel();
|
|
||||||
$this->user->unBan();
|
|
||||||
|
|
||||||
if (! $userModel->update($this->user->id, $this->user)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', $userModel->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()
|
|
||||||
->route('user-list')
|
|
||||||
->with('message', lang('User.messages.unbanSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete(): RedirectResponse
|
|
||||||
{
|
|
||||||
$authorize = Services::authorization();
|
|
||||||
if ($authorize->inGroup('superadmin', $this->user->id)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('errors', [
|
|
||||||
lang('User.messages.deleteSuperAdminError', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
(new UserModel())->delete($this->user->id);
|
|
||||||
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('message', lang('User.messages.deleteSuccess', [
|
|
||||||
'username' => $this->user->username,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -82,7 +82,7 @@ class VideoClipsController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
return view('episode/video_clips_list', $data);
|
return view('episode/video_clips_list', $data);
|
||||||
|
@ -99,7 +99,7 @@ class VideoClipsController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
2 => $videoClip->title,
|
2 => $videoClip->title,
|
||||||
]);
|
]);
|
||||||
|
@ -114,7 +114,7 @@ class VideoClipsController extends BaseController
|
||||||
];
|
];
|
||||||
|
|
||||||
replace_breadcrumb_params([
|
replace_breadcrumb_params([
|
||||||
0 => $this->podcast->title,
|
0 => $this->podcast->at_handle,
|
||||||
1 => $this->episode->title,
|
1 => $this->episode->title,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ return [
|
||||||
'publish-date-edit' => 'edit publication date',
|
'publish-date-edit' => 'edit publication date',
|
||||||
'unpublish' => 'unpublish',
|
'unpublish' => 'unpublish',
|
||||||
'delete' => 'delete',
|
'delete' => 'delete',
|
||||||
|
'remove' => 'remove',
|
||||||
'fediverse' => 'fediverse',
|
'fediverse' => 'fediverse',
|
||||||
'block-lists' => 'block lists',
|
'block-lists' => 'block lists',
|
||||||
'users' => 'users',
|
'users' => 'users',
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'edit_roles' => "Edit {username}'s roles",
|
|
||||||
'forcePassReset' => 'Force pass reset',
|
|
||||||
'ban' => 'Ban',
|
|
||||||
'unban' => 'Unban',
|
|
||||||
'delete' => 'Delete',
|
|
||||||
'create' => 'New user',
|
|
||||||
'view' => "{username}'s info",
|
|
||||||
'all_users' => 'All users',
|
|
||||||
'list' => [
|
|
||||||
'user' => 'User',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'banned' => 'Banned?',
|
|
||||||
],
|
|
||||||
'form' => [
|
|
||||||
'email' => 'Email',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => 'Password',
|
|
||||||
'new_password' => 'New Password',
|
|
||||||
'roles' => 'Roles',
|
|
||||||
'permissions' => 'Permissions',
|
|
||||||
'submit_create' => 'Create user',
|
|
||||||
'submit_edit' => 'Save',
|
|
||||||
'submit_password_change' => 'Change!',
|
|
||||||
],
|
|
||||||
'roles' => [
|
|
||||||
'superadmin' => 'Super admin',
|
|
||||||
],
|
|
||||||
'messages' => [
|
|
||||||
'createSuccess' =>
|
|
||||||
'User created successfully! {username} will be prompted with a password reset upon first authentication.',
|
|
||||||
'rolesEditSuccess' =>
|
|
||||||
"{username}'s roles have been successfully updated.",
|
|
||||||
'forcePassResetSuccess' =>
|
|
||||||
'{username} will be prompted with a password reset upon next visit.',
|
|
||||||
'banSuccess' => '{username} has been banned.',
|
|
||||||
'unbanSuccess' => '{username} has been unbanned.',
|
|
||||||
'editOwnerError' =>
|
|
||||||
'{username} is the instance owner, you cannot edit its roles.',
|
|
||||||
'banSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply ban a superadmin…',
|
|
||||||
'deleteSuperAdminError' =>
|
|
||||||
'{username} is a superadmin, one does not simply delete a superadmin…',
|
|
||||||
'deleteSuccess' => '{username} has been deleted.',
|
|
||||||
],
|
|
||||||
];
|
|
|
@ -20,9 +20,9 @@ class Analytics extends BaseConfig
|
||||||
* @var array<string, string>
|
* @var array<string, string>
|
||||||
*/
|
*/
|
||||||
public array $routeFilters = [
|
public array $routeFilters = [
|
||||||
'analytics-full-data' => 'permission:podcasts-view,podcast-view',
|
'analytics-full-data' => 'permission:podcast#.view',
|
||||||
'analytics-data' => 'permission:podcasts-view,podcast-view',
|
'analytics-data' => 'permission:podcast#.view',
|
||||||
'analytics-filtered-data' => 'permission:podcasts-view,podcast-view',
|
'analytics-filtered-data' => 'permission:podcast#.view',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth;
|
||||||
|
|
||||||
|
use CodeIgniter\Router\RouteCollection;
|
||||||
|
use CodeIgniter\Shield\Auth as ShieldAuth;
|
||||||
|
|
||||||
|
class Auth extends ShieldAuth
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Will set the routes in your application to use
|
||||||
|
* the Shield auth routes.
|
||||||
|
*
|
||||||
|
* Usage (in Config/Routes.php):
|
||||||
|
* - auth()->routes($routes);
|
||||||
|
* - auth()->routes($routes, ['except' => ['login', 'register']])
|
||||||
|
*/
|
||||||
|
public function routes(RouteCollection &$routes, array $config = []): void
|
||||||
|
{
|
||||||
|
$authRoutes = config('AuthRoutes')
|
||||||
|
->routes;
|
||||||
|
|
||||||
|
$routes->group(config('Auth')->gateway, [
|
||||||
|
'namespace' => 'Modules\Auth\Controllers',
|
||||||
|
], static function (RouteCollection $routes) use ($authRoutes, $config): void {
|
||||||
|
foreach ($authRoutes as $name => $row) {
|
||||||
|
if (! isset($config['except']) || ! in_array($name, $config['except'], true)) {
|
||||||
|
foreach ($row as $params) {
|
||||||
|
$options = isset($params[3])
|
||||||
|
? [
|
||||||
|
'as' => $params[3],
|
||||||
|
]
|
||||||
|
: null;
|
||||||
|
$routes->{$params[0]}($params[1], $params[2], $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Modules\Auth\Authorization;
|
|
||||||
|
|
||||||
use Myth\Auth\Authorization\FlatAuthorization as MythAuthFlatAuthorization;
|
|
||||||
|
|
||||||
class FlatAuthorization extends MythAuthFlatAuthorization
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The group model to use. Usually the class noted below (or an extension thereof) but can be any compatible
|
|
||||||
* CodeIgniter Model.
|
|
||||||
*
|
|
||||||
* @var PermissionModel
|
|
||||||
*/
|
|
||||||
protected $permissionModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks a group to see if they have the specified permission.
|
|
||||||
*/
|
|
||||||
public function groupHasPermission(int | string $permission, int $groupId): bool
|
|
||||||
{
|
|
||||||
// Get the Permission ID
|
|
||||||
$permissionId = $this->getPermissionID($permission);
|
|
||||||
|
|
||||||
if (! is_numeric($permissionId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->permissionModel->doesGroupHavePermission($groupId, $permissionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes user part of given groups.
|
|
||||||
*
|
|
||||||
* @param array<string, string> $groups Either collection of ID or names
|
|
||||||
*/
|
|
||||||
public function setUserGroups(int $userId, array $groups = []): bool
|
|
||||||
{
|
|
||||||
// remove user from all groups before resetting it in new groups
|
|
||||||
$this->groupModel->removeUserFromAllGroups($userId);
|
|
||||||
|
|
||||||
if ($groups === []) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
$this->addUserToGroup($userId, $group);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Modules\Auth\Authorization;
|
|
||||||
|
|
||||||
use Myth\Auth\Authorization\GroupModel as MythAuthGroupModel;
|
|
||||||
|
|
||||||
class GroupModel extends MythAuthGroupModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
public function getContributorRoles(): array
|
|
||||||
{
|
|
||||||
return $this->select('auth_groups.*')
|
|
||||||
->like('name', 'podcast_', 'after')
|
|
||||||
->findAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
public function getUserRoles(): array
|
|
||||||
{
|
|
||||||
return $this->select('auth_groups.*')
|
|
||||||
->notLike('name', 'podcast_', 'after')
|
|
||||||
->findAll();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Modules\Auth\Authorization;
|
|
||||||
|
|
||||||
use Myth\Auth\Authorization\PermissionModel as MythAuthPermissionModel;
|
|
||||||
|
|
||||||
class PermissionModel extends MythAuthPermissionModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Checks to see if a user, or one of their groups, has a specific permission.
|
|
||||||
*/
|
|
||||||
public function doesGroupHavePermission(int $groupId, int $permissionId): bool
|
|
||||||
{
|
|
||||||
// Check group permissions and take advantage of caching
|
|
||||||
$groupPerms = $this->getPermissionsForGroup($groupId);
|
|
||||||
|
|
||||||
return count($groupPerms) &&
|
|
||||||
array_key_exists($permissionId, $groupPerms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all permissions for a group in a way that can be easily used to check against:
|
|
||||||
*
|
|
||||||
* [ id => name, id => name ]
|
|
||||||
*
|
|
||||||
* @return array<int, string>
|
|
||||||
*/
|
|
||||||
public function getPermissionsForGroup(int $groupId): array
|
|
||||||
{
|
|
||||||
$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')
|
|
||||||
->where('group_id', $groupId)
|
|
||||||
->get()
|
|
||||||
->getResultObject();
|
|
||||||
|
|
||||||
$found = [];
|
|
||||||
foreach ($groupPermissions as $row) {
|
|
||||||
$found[$row->id] = strtolower($row->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
cache()
|
|
||||||
->save($cacheName, $found, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $found;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Commands;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use CodeIgniter\CLI\BaseCommand;
|
||||||
|
use CodeIgniter\CLI\CLI;
|
||||||
|
use CodeIgniter\View\Table;
|
||||||
|
use Config\Services;
|
||||||
|
use League\HTMLToMarkdown\Converter\TableConverter;
|
||||||
|
use League\HTMLToMarkdown\HtmlConverter;
|
||||||
|
use Modules\Auth\Config\AuthGroups;
|
||||||
|
|
||||||
|
class RolesDoc extends BaseCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private const COMMENT_BLOCK_IDS = [
|
||||||
|
'instance_roles' => 'AUTH-INSTANCE-ROLES-LIST',
|
||||||
|
'instance_permissions' => 'AUTH-INSTANCE-PERMISSIONS-LIST',
|
||||||
|
'podcast_roles' => 'AUTH-PODCAST-ROLES-LIST',
|
||||||
|
'podcast_permissions' => 'AUTH-PODCAST-PERMISSIONS-LIST',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $group = 'auth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name = 'auth:generate-doc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Generates the html table references for roles and permissions in the docs.';
|
||||||
|
|
||||||
|
public function run(array $params): void
|
||||||
|
{
|
||||||
|
// loop over all files in path
|
||||||
|
$defaultFile = glob(ROOTPATH . 'docs/src/getting-started/auth.md');
|
||||||
|
$localizedFiles = glob(ROOTPATH . 'docs/src/**/getting-started/auth.md') ?? [];
|
||||||
|
$files = array_merge($defaultFile, $localizedFiles);
|
||||||
|
CLI::write(implode(', ', $files));
|
||||||
|
|
||||||
|
if ($files === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$locale = $this->detectLocaleFromPath($file);
|
||||||
|
$language = Services::language();
|
||||||
|
$language->setLocale($locale);
|
||||||
|
|
||||||
|
$authGroups = new AuthGroups();
|
||||||
|
|
||||||
|
$fileContents = file_get_contents($file);
|
||||||
|
|
||||||
|
foreach (self::COMMENT_BLOCK_IDS as $key => $block_id) {
|
||||||
|
$pattern = '/(<!--\s' . $block_id . ':START.*-->)[\S\s]*(<!--\s' . $block_id . ':END.*-->)/';
|
||||||
|
|
||||||
|
$handleInjectMethod = 'handle' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
|
||||||
|
|
||||||
|
$fileContents = $this->{$handleInjectMethod}($authGroups, $fileContents, $pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the contents back to the file
|
||||||
|
file_put_contents($file, $fileContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleInstanceRoles($authGroups, string $fileContents, string $pattern): string
|
||||||
|
{
|
||||||
|
$instanceMatrix = $authGroups->matrix;
|
||||||
|
return $this->renderCommentBlock(
|
||||||
|
$fileContents,
|
||||||
|
$pattern,
|
||||||
|
['role', 'description', 'permissions'],
|
||||||
|
$authGroups->instanceGroups,
|
||||||
|
static function ($table, $key, $value) use ($instanceMatrix): void {
|
||||||
|
$table->addRow($value['title'], $value['description'], implode(', ', $instanceMatrix[$key]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleInstancePermissions($authGroups, string $fileContents, string $pattern): string
|
||||||
|
{
|
||||||
|
return $this->renderCommentBlock(
|
||||||
|
$fileContents,
|
||||||
|
$pattern,
|
||||||
|
['permission', 'description'],
|
||||||
|
$authGroups->instancePermissions,
|
||||||
|
static function ($table, $key, $value): void {
|
||||||
|
$table->addRow($key, $value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handlePodcastRoles($authGroups, string $fileContents, string $pattern): string
|
||||||
|
{
|
||||||
|
$podcastMatrix = $authGroups->podcastMatrix;
|
||||||
|
return $this->renderCommentBlock(
|
||||||
|
$fileContents,
|
||||||
|
$pattern,
|
||||||
|
['role', 'description', 'permissions'],
|
||||||
|
$authGroups->podcastGroups,
|
||||||
|
static function ($table, $key, $value) use ($podcastMatrix): void {
|
||||||
|
$table->addRow($value['title'], $value['description'], implode(', ', $podcastMatrix[$key]));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handlePodcastPermissions($authGroups, string $fileContents, string $pattern): string
|
||||||
|
{
|
||||||
|
return $this->renderCommentBlock(
|
||||||
|
$fileContents,
|
||||||
|
$pattern,
|
||||||
|
['permission', 'description'],
|
||||||
|
$authGroups->podcastPermissions,
|
||||||
|
static function ($table, $key, $value): void {
|
||||||
|
$table->addRow($key, $value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderCommentBlock(
|
||||||
|
string $fileContents,
|
||||||
|
string $pattern,
|
||||||
|
array $tableHeading,
|
||||||
|
array $data,
|
||||||
|
Closure $callback
|
||||||
|
): string {
|
||||||
|
// check if it has the start and end comments to insert roles table
|
||||||
|
// looking for <AUTH-INSTANCE-ROLES-LIST:START> and <AUTH-INSTANCE-ROLES-LIST:END>
|
||||||
|
|
||||||
|
$hasInstanceInsertComments = preg_match($pattern, $fileContents);
|
||||||
|
|
||||||
|
if (! $hasInstanceInsertComments) {
|
||||||
|
return $fileContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare role table
|
||||||
|
$table = new Table();
|
||||||
|
$table->setHeading($tableHeading);
|
||||||
|
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$callback($table, $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$converter = new HtmlConverter();
|
||||||
|
$converter->getEnvironment()
|
||||||
|
->addConverter(new TableConverter());
|
||||||
|
$markdownTable = $converter->convert($table->generate());
|
||||||
|
|
||||||
|
// insert table between block comments
|
||||||
|
$newFileContents = preg_replace(
|
||||||
|
$pattern,
|
||||||
|
'${1}' . PHP_EOL . PHP_EOL . $markdownTable . PHP_EOL . PHP_EOL . '${2}',
|
||||||
|
$fileContents
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($newFileContents === null) {
|
||||||
|
return $fileContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newFileContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function detectLocaleFromPath($filePath): string
|
||||||
|
{
|
||||||
|
preg_match('~docs\/src\/(?:([a-z]{2}(?:-[A-Za-z]{2,})?)\/)getting-started\/auth\.md~', $filePath, $match);
|
||||||
|
|
||||||
|
if ($match === []) {
|
||||||
|
return 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $match[1];
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,46 +4,99 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Modules\Auth\Config;
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
use Myth\Auth\Config\Auth as MythAuthConfig;
|
use CodeIgniter\Shield\Authentication\Actions\ActionInterface;
|
||||||
|
use CodeIgniter\Shield\Config\Auth as ShieldAuth;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
class Auth extends MythAuthConfig
|
class Auth extends ShieldAuth
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------------
|
* ////////////////// AUTHENTICATION //////////////////
|
||||||
* Views used by Auth Controllers
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*
|
*
|
||||||
* @var array<string, string>
|
* @var array<string, string>
|
||||||
*/
|
*/
|
||||||
public $views = [
|
public array $views = [
|
||||||
'login' => 'login',
|
'login' => 'login',
|
||||||
'register' => 'register',
|
'register' => 'register',
|
||||||
'forgot' => 'forgot',
|
'layout' => '_layout',
|
||||||
'reset' => 'reset',
|
'action_email_2fa' => 'email_2fa_show',
|
||||||
'emailForgot' => 'emails/forgot',
|
'action_email_2fa_verify' => 'email_2fa_verify',
|
||||||
'emailActivation' => 'emails/activation',
|
'action_email_2fa_email' => 'emails/email_2fa_email',
|
||||||
|
'action_email_activate_show' => 'email_activate_show',
|
||||||
|
'action_email_activate_email' => 'emails/email_activate_email',
|
||||||
|
'magic-link-login' => 'magic_link_form',
|
||||||
|
'magic-link-message' => 'magic_link_message',
|
||||||
|
'magic-link-email' => 'emails/magic_link_email',
|
||||||
|
'magic-link-set-password' => 'magic_link_set_password',
|
||||||
|
'welcome-email' => 'emails/welcome_email',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* Layout for the views to extend
|
* Redirect urLs
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
|
* The default URL that a user will be redirected to after
|
||||||
|
* various auth actions. If you need more flexibility you can
|
||||||
|
* override the `getUrl()` method to apply any logic you may need.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var array<string, string>
|
||||||
*/
|
*/
|
||||||
public $viewLayout = '_layout';
|
public array $redirects = [
|
||||||
|
'register' => '/',
|
||||||
|
'login' => '/',
|
||||||
|
'logout' => 'login',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* Allow User Registration
|
* Authentication Actions
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
* When enabled (default) any unregistered user may apply for a new
|
* Specifies the class that represents an action to take after
|
||||||
* account. If you disable registration you may need to ensure your
|
* the user logs in or registers a new account at the site.
|
||||||
* controllers and views know not to offer registration.
|
|
||||||
*
|
*
|
||||||
* @var bool
|
* You must register actions in the order of the actions to be performed.
|
||||||
|
*
|
||||||
|
* Available actions with Shield:
|
||||||
|
* - register: 'CodeIgniter\Shield\Authentication\Actions\EmailActivator'
|
||||||
|
* - login: 'CodeIgniter\Shield\Authentication\Actions\Email2FA'
|
||||||
|
*
|
||||||
|
* @var array<string, class-string<ActionInterface>|null>
|
||||||
*/
|
*/
|
||||||
public $allowRegistration = false;
|
public array $actions = [
|
||||||
|
'register' => null,
|
||||||
|
'login' => null,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Allow Registration
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Determines whether users can register for the site.
|
||||||
|
*/
|
||||||
|
public bool $allowRegistration = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Welcome Link Lifetime
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Specifies the amount of time, in seconds, that a welcome link is valid.
|
||||||
|
* You can use Time Constants or any desired number.
|
||||||
|
*/
|
||||||
|
public int $welcomeLinkLifetime = 48 * HOUR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* User Provider
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The name of the class that handles user persistence.
|
||||||
|
* By default, this is the included UserModel, which
|
||||||
|
* works with any of the database engines supported by CodeIgniter.
|
||||||
|
* You can change it as long as they adhere to the
|
||||||
|
* CodeIgniter\Shield\Models\UserModel.
|
||||||
|
*
|
||||||
|
* @var class-string<UserModel>
|
||||||
|
*/
|
||||||
|
public string $userProvider = UserModel::class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------------
|
||||||
|
@ -52,4 +105,28 @@ class Auth extends MythAuthConfig
|
||||||
* Defines a base route for all authentication related pages
|
* Defines a base route for all authentication related pages
|
||||||
*/
|
*/
|
||||||
public string $gateway = 'cp-auth';
|
public string $gateway = 'cp-auth';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$adminGateway = config('Admin')
|
||||||
|
->gateway;
|
||||||
|
|
||||||
|
$this->redirects = [
|
||||||
|
'register' => $adminGateway,
|
||||||
|
'login' => $adminGateway,
|
||||||
|
'logout' => $adminGateway,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL that a user should be redirected to after a successful login.
|
||||||
|
*
|
||||||
|
* Redirects to the set-password form if magicLogin
|
||||||
|
*/
|
||||||
|
public function loginRedirect(): string
|
||||||
|
{
|
||||||
|
$url = session('magicLogin') ? route_to('magic-link-set-password') : setting('Auth.redirects')['login'];
|
||||||
|
|
||||||
|
return $this->getUrl($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
|
use App\Models\PodcastModel;
|
||||||
|
use CodeIgniter\Shield\Config\AuthGroups as ShieldAuthGroups;
|
||||||
|
|
||||||
|
class AuthGroups extends ShieldAuthGroups
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Default Group
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The group that a newly registered user is added to.
|
||||||
|
*/
|
||||||
|
public string $defaultGroup = 'podcaster';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Most powerful Group
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The group that a has the most permissions.
|
||||||
|
*/
|
||||||
|
public string $mostPowerfulGroup = 'superadmin';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Default Podcast Group
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The group that a newly registered user is added to.
|
||||||
|
*/
|
||||||
|
public string $defaultPodcastGroup = 'guest';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Most powerful Podcast Group
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The group that a has the most permissions on a podcast.
|
||||||
|
*/
|
||||||
|
public string $mostPowerfulPodcastGroup = 'admin';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Groups
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The available authentication systems, listed
|
||||||
|
* with alias and class name. These can be referenced
|
||||||
|
* by alias in the auth helper:
|
||||||
|
* auth('api')->attempt($credentials);
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, string>>
|
||||||
|
*/
|
||||||
|
public array $groups = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Permissions
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* The available permissions in the system. Each system is defined
|
||||||
|
* where the key is the
|
||||||
|
*
|
||||||
|
* If a permission is not listed here it cannot be used.
|
||||||
|
*
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
public array $permissions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Permissions Matrix
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Maps permissions to groups.
|
||||||
|
* @var array<string, array<string>>
|
||||||
|
*/
|
||||||
|
public array $matrix = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, string>>
|
||||||
|
*/
|
||||||
|
public array $instanceGroups = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
public array $instancePermissions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, string>>
|
||||||
|
*/
|
||||||
|
public array $podcastGroups = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
public array $podcastPermissions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public array $instanceBaseGroups = ['superadmin', 'manager', 'podcaster'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public array $instanceBasePermissions = [
|
||||||
|
'admin.access',
|
||||||
|
'admin.settings',
|
||||||
|
'users.manage',
|
||||||
|
'persons.manage',
|
||||||
|
'pages.manage',
|
||||||
|
'podcasts.view',
|
||||||
|
'podcasts.create',
|
||||||
|
'podcasts.import',
|
||||||
|
'fediverse.manage-blocks',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string>>
|
||||||
|
*/
|
||||||
|
public array $instanceMatrix = [
|
||||||
|
'superadmin' => [
|
||||||
|
'admin.*',
|
||||||
|
'podcasts.*',
|
||||||
|
'users.manage',
|
||||||
|
'persons.manage',
|
||||||
|
'pages.manage',
|
||||||
|
'fediverse.manage-blocks',
|
||||||
|
],
|
||||||
|
'manager' => ['podcasts.create', 'podcasts.import', 'persons.manage', 'pages.manage'],
|
||||||
|
'podcaster' => ['admin.access'],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public array $podcastBaseGroups = ['admin', 'editor', 'author', 'guest'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public array $podcastBasePermissions = [
|
||||||
|
'view',
|
||||||
|
'edit',
|
||||||
|
'delete',
|
||||||
|
'manage-import',
|
||||||
|
'manage-persons',
|
||||||
|
'manage-subscriptions',
|
||||||
|
'manage-contributors',
|
||||||
|
'manage-platforms',
|
||||||
|
'manage-publications',
|
||||||
|
'interact-as',
|
||||||
|
'episodes.view',
|
||||||
|
'episodes.create',
|
||||||
|
'episodes.edit',
|
||||||
|
'episodes.delete',
|
||||||
|
'episodes.manage-persons',
|
||||||
|
'episodes.manage-clips',
|
||||||
|
'episodes.manage-publications',
|
||||||
|
'episodes.manage-comments',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string[]>
|
||||||
|
*/
|
||||||
|
public array $podcastMatrix = [
|
||||||
|
'admin' => ['*'],
|
||||||
|
'editor' => [
|
||||||
|
'view',
|
||||||
|
'edit',
|
||||||
|
'manage-import',
|
||||||
|
'manage-persons',
|
||||||
|
'manage-platforms',
|
||||||
|
'manage-publications',
|
||||||
|
'interact-as',
|
||||||
|
'episodes.view',
|
||||||
|
'episodes.create',
|
||||||
|
'episodes.edit',
|
||||||
|
'episodes.delete',
|
||||||
|
'episodes.manage-persons',
|
||||||
|
'episodes.manage-clips',
|
||||||
|
'episodes.manage-publications',
|
||||||
|
'episodes.manage-comments',
|
||||||
|
],
|
||||||
|
'author' => [
|
||||||
|
'view',
|
||||||
|
'manage-persons',
|
||||||
|
'episodes.view',
|
||||||
|
'episodes.create',
|
||||||
|
'episodes.edit',
|
||||||
|
'episodes.manage-persons',
|
||||||
|
'episodes.manage-clips',
|
||||||
|
],
|
||||||
|
'guest' => ['view', 'episodes.view'],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill groups, permissions and matrix based on
|
||||||
|
*/
|
||||||
|
public function __construct($locale = null)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
foreach ($this->instanceBaseGroups as $group) {
|
||||||
|
$this->instanceGroups[$group] = [
|
||||||
|
'title' => lang("Auth.instance_groups.{$group}.title"),
|
||||||
|
'description' => lang("Auth.instance_groups.{$group}.description"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->groups = $this->instanceGroups;
|
||||||
|
|
||||||
|
foreach ($this->instanceBasePermissions as $permission) {
|
||||||
|
$this->instancePermissions[$permission] = lang("Auth.instance_permissions.{$permission}");
|
||||||
|
$this->permissions[$permission] = lang("Auth.instance_permissions.{$permission}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->matrix = $this->instanceMatrix;
|
||||||
|
|
||||||
|
$this->generateBasePodcastAuthorizations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each podcast, include podcast groups, permissions, and matrix into $groups, $permissions, and $matrix
|
||||||
|
* attributes.
|
||||||
|
*/
|
||||||
|
$podcasts = (new PodcastModel())->findAll();
|
||||||
|
foreach ($podcasts as $podcast) {
|
||||||
|
$this->generatePodcastAuthorizations($podcast->id, $locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateBasePodcastAuthorizations(): void
|
||||||
|
{
|
||||||
|
foreach ($this->podcastBaseGroups as $group) {
|
||||||
|
$this->podcastGroups[$group] = [
|
||||||
|
'title' => lang("Auth.podcast_groups.{$group}.title", [
|
||||||
|
'id' => '{id}',
|
||||||
|
]),
|
||||||
|
'description' => lang("Auth.podcast_groups.{$group}.description", [
|
||||||
|
'id' => '{id}',
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->podcastBasePermissions as $permission) {
|
||||||
|
$this->podcastPermissions[$permission] = lang("Auth.podcast_permissions.{$permission}", [
|
||||||
|
'id' => '{id}',
|
||||||
|
]);
|
||||||
|
$this->permissions[$permission] = lang("Auth.podcast_permissions.{$permission}", [
|
||||||
|
'id' => '{id}',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePodcastAuthorizations(int $podcastId): void
|
||||||
|
{
|
||||||
|
foreach ($this->podcastBaseGroups as $group) {
|
||||||
|
$podcastGroup = 'podcast#' . $podcastId . '-' . $group;
|
||||||
|
$this->groups[$podcastGroup] = [
|
||||||
|
'title' => lang("Auth.podcast_groups.{$group}.title", [
|
||||||
|
'id' => $podcastId,
|
||||||
|
]),
|
||||||
|
'description' => lang("Auth.podcast_groups.{$group}.description", [
|
||||||
|
'id' => $podcastId,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->podcastBasePermissions as $permission) {
|
||||||
|
$podcastPermission = 'podcast#' . $podcastId . '.' . $permission;
|
||||||
|
$this->permissions[$podcastPermission] = lang("Auth.podcast_permissions.{$permission}", [
|
||||||
|
'id' => $podcastId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->podcastMatrix as $group => $permissionWildcards) {
|
||||||
|
$podcastGroup = 'podcast#' . $podcastId . '-' . $group;
|
||||||
|
foreach ($permissionWildcards as $permissionWildcard) {
|
||||||
|
$podcastPermissionWildcard = 'podcast#' . $podcastId . '.' . $permissionWildcard;
|
||||||
|
$this->matrix[$podcastGroup][] = $podcastPermissionWildcard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
|
use CodeIgniter\Shield\Config\AuthRoutes as ShieldAuthRoutes;
|
||||||
|
|
||||||
|
class AuthRoutes extends ShieldAuthRoutes
|
||||||
|
{
|
||||||
|
public array $routes = [
|
||||||
|
'register' => [
|
||||||
|
['get', 'register', 'RegisterController::registerView', 'register'],
|
||||||
|
['post', 'register', 'RegisterController::registerAction'],
|
||||||
|
],
|
||||||
|
'login' => [
|
||||||
|
['get', 'login', 'LoginController::loginView', 'login'],
|
||||||
|
['post', 'login', 'LoginController::loginAction'],
|
||||||
|
],
|
||||||
|
'magic-link' => [
|
||||||
|
[
|
||||||
|
'get',
|
||||||
|
'login/magic-link',
|
||||||
|
'MagicLinkController::loginView',
|
||||||
|
'magic-link', // Route name
|
||||||
|
],
|
||||||
|
['post', 'login/magic-link', 'MagicLinkController::loginAction'],
|
||||||
|
[
|
||||||
|
'get',
|
||||||
|
'login/verify-magic-link',
|
||||||
|
'MagicLinkController::verify',
|
||||||
|
'verify-magic-link', // Route name
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'logout' => [['get', 'logout', 'LoginController::logoutAction', 'logout']],
|
||||||
|
'auth-actions' => [
|
||||||
|
['get', 'auth/a/show', 'ActionController::show', 'auth-action-show'],
|
||||||
|
['post', 'auth/a/handle', 'ActionController::handle', 'auth-action-handle'],
|
||||||
|
['post', 'auth/a/verify', 'ActionController::verify', 'auth-action-verify'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
|
use CodeIgniter\Events\Events;
|
||||||
|
use CodeIgniter\Shield\Entities\User;
|
||||||
|
|
||||||
|
Events::on('logout', static function (User $user): void {
|
||||||
|
helper('auth');
|
||||||
|
// remove user's interact_as_actor session
|
||||||
|
remove_interact_as_actor();
|
||||||
|
});
|
|
@ -6,52 +6,134 @@ namespace Modules\Auth\Config;
|
||||||
|
|
||||||
$routes = service('routes');
|
$routes = service('routes');
|
||||||
|
|
||||||
/**
|
service('auth')
|
||||||
* Overwriting Myth:auth routes file
|
->routes($routes);
|
||||||
*/
|
|
||||||
|
// Admin routes for users and podcast contributors
|
||||||
$routes->group(
|
$routes->group(
|
||||||
config('Auth')
|
config('Admin')
|
||||||
->gateway,
|
->gateway,
|
||||||
[
|
[
|
||||||
'namespace' => 'Modules\Auth\Controllers',
|
'namespace' => 'Modules\Auth\Controllers',
|
||||||
],
|
],
|
||||||
static function ($routes): void {
|
static function ($routes): void {
|
||||||
// Login/out
|
$routes->get('magic-link-set-password', 'MagicLinkController::setPasswordView', [
|
||||||
$routes->get('login', 'AuthController::login', [
|
'as' => 'magic-link-set-password',
|
||||||
'as' => 'login',
|
|
||||||
]);
|
]);
|
||||||
$routes->post('login', 'AuthController::attemptLogin');
|
$routes->post('magic-link-set-password', 'MagicLinkController::setPasswordAction');
|
||||||
$routes->get('logout', 'AuthController::logout', [
|
|
||||||
'as' => 'logout',
|
$routes->post('interact-as-actor', 'InteractController::attemptInteractAsActor', [
|
||||||
]);
|
|
||||||
// Registration
|
|
||||||
$routes->get('register', 'AuthController::register', [
|
|
||||||
'as' => 'register',
|
|
||||||
]);
|
|
||||||
$routes->post('register', 'AuthController::attemptRegister');
|
|
||||||
// Activation
|
|
||||||
$routes->get('activate-account', 'AuthController::activateAccount', [
|
|
||||||
'as' => 'activate-account',
|
|
||||||
]);
|
|
||||||
$routes->get(
|
|
||||||
'resend-activate-account',
|
|
||||||
'AuthController::resendActivateAccount',
|
|
||||||
[
|
|
||||||
'as' => 'resend-activate-account',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// Forgot/Resets
|
|
||||||
$routes->get('forgot', 'AuthController::forgotPassword', [
|
|
||||||
'as' => 'forgot',
|
|
||||||
]);
|
|
||||||
$routes->post('forgot', 'AuthController::attemptForgot');
|
|
||||||
$routes->get('reset-password', 'AuthController::resetPassword', [
|
|
||||||
'as' => 'reset-password',
|
|
||||||
]);
|
|
||||||
$routes->post('reset-password', 'AuthController::attemptReset');
|
|
||||||
// interacting as an actor
|
|
||||||
$routes->post('interact-as-actor', 'AuthController::attemptInteractAsActor', [
|
|
||||||
'as' => 'interact-as-actor',
|
'as' => 'interact-as-actor',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Users
|
||||||
|
$routes->group('users', static function ($routes): void {
|
||||||
|
$routes->get('/', 'UserController::list', [
|
||||||
|
'as' => 'user-list',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->get('new', 'UserController::create', [
|
||||||
|
'as' => 'user-create',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->post('new', 'UserController::attemptCreate', [
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
// User
|
||||||
|
$routes->group('(:num)', static function ($routes): void {
|
||||||
|
$routes->get('/', 'UserController::view/$1', [
|
||||||
|
'as' => 'user-view',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->get('edit', 'UserController::edit/$1', [
|
||||||
|
'as' => 'user-edit',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->post('edit', 'UserController::attemptEdit/$1', [
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->get('delete', 'UserController::delete/$1', [
|
||||||
|
'as' => 'user-delete',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
$routes->post('delete', 'UserController::attemptDelete/$1', [
|
||||||
|
'as' => 'user-delete',
|
||||||
|
'filter' => 'permission:users.manage',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// My account
|
||||||
|
$routes->group('my-account', static function ($routes): void {
|
||||||
|
$routes->get('/', 'MyAccountController', [
|
||||||
|
'as' => 'my-account',
|
||||||
|
]);
|
||||||
|
$routes->get('change-password', 'MyAccountController::changePassword', [
|
||||||
|
'as' => 'change-password',
|
||||||
|
],);
|
||||||
|
$routes->post('change-password', 'MyAccountController::attemptChange');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Podcast contributors
|
||||||
|
$routes->group('podcasts/(:num)/contributors', static function ($routes): void {
|
||||||
|
$routes->get('/', 'ContributorController::list/$1', [
|
||||||
|
'as' => 'contributor-list',
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
]);
|
||||||
|
$routes->get('add', 'ContributorController::add/$1', [
|
||||||
|
'as' => 'contributor-add',
|
||||||
|
'filter' => 'permission:podcast#.manage-contributors',
|
||||||
|
]);
|
||||||
|
$routes->post(
|
||||||
|
'add',
|
||||||
|
'ContributorController::attemptAdd/$1',
|
||||||
|
[
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
// Contributor
|
||||||
|
$routes->group('(:num)', static function ($routes): void {
|
||||||
|
$routes->get('/', 'ContributorController::view/$1/$2', [
|
||||||
|
'as' => 'contributor-view',
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
]);
|
||||||
|
$routes->get(
|
||||||
|
'edit',
|
||||||
|
'ContributorController::edit/$1/$2',
|
||||||
|
[
|
||||||
|
'as' => 'contributor-edit',
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$routes->post(
|
||||||
|
'edit',
|
||||||
|
'ContributorController::attemptEdit/$1/$2',
|
||||||
|
[
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$routes->get(
|
||||||
|
'remove',
|
||||||
|
'ContributorController::remove/$1/$2',
|
||||||
|
[
|
||||||
|
'as' => 'contributor-remove',
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$routes->post(
|
||||||
|
'remove',
|
||||||
|
'ContributorController::attemptRemove/$1/$2',
|
||||||
|
[
|
||||||
|
'filter' =>
|
||||||
|
'permission:podcast#.manage-contributors',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,87 +4,23 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Modules\Auth\Config;
|
namespace Modules\Auth\Config;
|
||||||
|
|
||||||
use App\Models\UserModel;
|
use CodeIgniter\Shield\Authentication\Authentication;
|
||||||
use CodeIgniter\Config\BaseService;
|
use Config\Services as BaseService;
|
||||||
use CodeIgniter\Model;
|
use Modules\Auth\Auth;
|
||||||
use Modules\Auth\Authorization\FlatAuthorization;
|
|
||||||
use Modules\Auth\Authorization\GroupModel;
|
|
||||||
use Modules\Auth\Authorization\PermissionModel;
|
|
||||||
use Myth\Auth\Models\LoginModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Services Configuration file.
|
|
||||||
*
|
|
||||||
* Services are simply other classes/libraries that the system uses to do its job. This is used by CodeIgniter to allow
|
|
||||||
* the core of the framework to be swapped out easily without affecting the usage within the rest of your application.
|
|
||||||
*
|
|
||||||
* This file holds any application-specific services, or service overrides that you might need. An example has been
|
|
||||||
* included with the general method format you should use for your service methods. For more examples, see the core
|
|
||||||
* Services file at system/Config/Services.php.
|
|
||||||
*/
|
|
||||||
class Services extends BaseService
|
class Services extends BaseService
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* The base auth class
|
||||||
*/
|
*/
|
||||||
public static function authentication(
|
public static function auth(bool $getShared = true): Auth
|
||||||
string $lib = 'local',
|
{
|
||||||
Model $userModel = null,
|
|
||||||
Model $loginModel = null,
|
|
||||||
bool $getShared = true
|
|
||||||
) {
|
|
||||||
if ($getShared) {
|
if ($getShared) {
|
||||||
return self::getSharedInstance('authentication', $lib, $userModel, $loginModel);
|
return self::getSharedInstance('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
// config() checks first in app/Config
|
|
||||||
$config = config('Auth');
|
$config = config('Auth');
|
||||||
|
|
||||||
$class = $config->authenticationLibs[$lib];
|
return new Auth(new Authentication($config));
|
||||||
|
|
||||||
$instance = new $class($config);
|
|
||||||
|
|
||||||
if ($userModel === null) {
|
|
||||||
$userModel = new UserModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($loginModel === null) {
|
|
||||||
$loginModel = new LoginModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $instance->setUserModel($userModel)
|
|
||||||
->setLoginModel($loginModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed|$this
|
|
||||||
*/
|
|
||||||
public static function authorization(
|
|
||||||
Model $groupModel = null,
|
|
||||||
Model $permissionModel = null,
|
|
||||||
Model $userModel = null,
|
|
||||||
bool $getShared = true
|
|
||||||
) {
|
|
||||||
if ($getShared) {
|
|
||||||
return self::getSharedInstance('authorization', $groupModel, $permissionModel, $userModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($groupModel === null) {
|
|
||||||
$groupModel = new GroupModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($permissionModel === null) {
|
|
||||||
$permissionModel = new PermissionModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @phpstan-ignore-next-line */
|
|
||||||
$instance = new FlatAuthorization($groupModel, $permissionModel);
|
|
||||||
|
|
||||||
if ($userModel === null) {
|
|
||||||
$userModel = new UserModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @phpstan-ignore-next-line */
|
|
||||||
return $instance->setUserModel($userModel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\Shield\Controllers\ActionController as ShieldActionController;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ViewThemes\Theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ActionController
|
||||||
|
*
|
||||||
|
* A generic controller to handle Authentication Actions.
|
||||||
|
*/
|
||||||
|
class ActionController extends ShieldActionController
|
||||||
|
{
|
||||||
|
public function initController(
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
LoggerInterface $logger
|
||||||
|
): void {
|
||||||
|
parent::initController($request, $response, $logger);
|
||||||
|
|
||||||
|
Theme::setTheme('auth');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,204 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Auth\Controllers;
|
|
||||||
|
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
|
||||||
use Modules\Auth\Entities\User;
|
|
||||||
use Myth\Auth\Controllers\AuthController as MythAuthController;
|
|
||||||
use ViewThemes\Theme;
|
|
||||||
|
|
||||||
class AuthController extends MythAuthController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* An array of helpers to be automatically loaded upon class instantiation.
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
protected $helpers = ['components'];
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
Theme::setTheme('auth');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to register a new user.
|
|
||||||
*/
|
|
||||||
public function attemptRegister(): RedirectResponse
|
|
||||||
{
|
|
||||||
// Check if registration is allowed
|
|
||||||
if (! $this->config->allowRegistration) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('error', lang('Auth.registerDisabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = model('UserModel');
|
|
||||||
|
|
||||||
// Validate here first, since some things,
|
|
||||||
// like the password, can only be validated properly here.
|
|
||||||
$rules = [
|
|
||||||
'username' =>
|
|
||||||
'required|alpha_numeric_space|min_length[3]|is_unique[users.username]',
|
|
||||||
'email' => 'required|valid_email|is_unique[users.email]',
|
|
||||||
'password' => 'required|strong_password',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', service('validation')->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the user
|
|
||||||
$allowedPostFields = array_merge(['password'], $this->config->validFields, $this->config->personalFields);
|
|
||||||
$user = new User($this->request->getPost($allowedPostFields));
|
|
||||||
|
|
||||||
$this->config->requireActivation === null
|
|
||||||
? $user->activate()
|
|
||||||
: $user->generateActivateHash();
|
|
||||||
|
|
||||||
// Ensure default group gets assigned if set
|
|
||||||
if ($this->config->defaultUserGroup !== null) {
|
|
||||||
$users = $users->withGroup($this->config->defaultUserGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $users->save($user)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', $users->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->config->requireActivation !== null) {
|
|
||||||
$activator = service('activator');
|
|
||||||
$sent = $activator->send($user);
|
|
||||||
|
|
||||||
if (! $sent) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('error', $activator->error() ?? lang('Auth.unknownError'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success!
|
|
||||||
return redirect()
|
|
||||||
->route('login')
|
|
||||||
->with('message', lang('Auth.activationSuccess'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success!
|
|
||||||
return redirect()
|
|
||||||
->route('login')
|
|
||||||
->with('message', lang('Auth.registerSuccess'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the code with the email and saves the new password, if they all pass validation.
|
|
||||||
*/
|
|
||||||
public function attemptReset(): RedirectResponse
|
|
||||||
{
|
|
||||||
if ($this->config->activeResetter === null) {
|
|
||||||
return redirect()
|
|
||||||
->route('login')
|
|
||||||
->with('error', lang('Auth.forgotDisabled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = model('UserModel');
|
|
||||||
|
|
||||||
// First things first - log the reset attempt.
|
|
||||||
$users->logResetAttempt(
|
|
||||||
$this->request->getPost('email'),
|
|
||||||
$this->request->getPost('token'),
|
|
||||||
$this->request->getIPAddress(),
|
|
||||||
(string) $this->request->getUserAgent(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$rules = [
|
|
||||||
'token' => 'required',
|
|
||||||
'email' => 'required|valid_email',
|
|
||||||
'password' => 'required|strong_password',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', $users->errors());
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $users
|
|
||||||
->where('email', $this->request->getPost('email'))
|
|
||||||
->where('reset_hash', $this->request->getPost('token'))
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($user === null) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->with('error', lang('Auth.forgotNoUser'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset token still valid?
|
|
||||||
if (
|
|
||||||
$user->reset_expires !== null &&
|
|
||||||
time() > $user->reset_expires->getTimestamp()
|
|
||||||
) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('error', lang('Auth.resetTokenExpired'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success! Save the new password, and cleanup the reset hash.
|
|
||||||
$user->password = $this->request->getPost('password');
|
|
||||||
$user->reset_hash = null;
|
|
||||||
$user->reset_at = date('Y-m-d H:i:s');
|
|
||||||
$user->reset_expires = null;
|
|
||||||
$user->force_pass_reset = false;
|
|
||||||
$users->save($user);
|
|
||||||
|
|
||||||
helper('auth');
|
|
||||||
|
|
||||||
// set interact_as_actor_id value
|
|
||||||
$userPodcasts = $user->podcasts;
|
|
||||||
if ($userPodcasts = $user->podcasts) {
|
|
||||||
set_interact_as_actor($userPodcasts[0]->actor_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()
|
|
||||||
->route('login')
|
|
||||||
->with('message', lang('Auth.resetSuccess'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function attemptInteractAsActor(): RedirectResponse
|
|
||||||
{
|
|
||||||
$rules = [
|
|
||||||
'actor_id' => 'required|numeric',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (! $this->validate($rules)) {
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withInput()
|
|
||||||
->with('errors', service('validation')->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
helper('auth');
|
|
||||||
|
|
||||||
set_interact_as_actor((int) $this->request->getPost('actor_id'));
|
|
||||||
|
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,243 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2022 Ad Aures
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use App\Entities\Podcast;
|
||||||
|
use App\Models\PodcastModel;
|
||||||
|
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||||
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
|
use CodeIgniter\Shield\Entities\User;
|
||||||
|
use Modules\Admin\Controllers\BaseController;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
|
class ContributorController extends BaseController
|
||||||
|
{
|
||||||
|
protected Podcast $podcast;
|
||||||
|
|
||||||
|
protected ?User $contributor;
|
||||||
|
|
||||||
|
public function _remap(string $method, string ...$params): mixed
|
||||||
|
{
|
||||||
|
if ($params === []) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($podcast = (new PodcastModel())->getPodcastById((int) $params[0])) === null) {
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->podcast = $podcast;
|
||||||
|
|
||||||
|
if (count($params) <= 1) {
|
||||||
|
return $this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($this->contributor = (new UserModel())->getPodcastContributor(
|
||||||
|
(int) $params[1],
|
||||||
|
(int) $params[0]
|
||||||
|
)) !== null) {
|
||||||
|
return $this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->at_handle,
|
||||||
|
]);
|
||||||
|
return view('contributor/list', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'contributor' => (new UserModel())->getPodcastContributor($this->contributor->id, $this->podcast->id),
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->at_handle,
|
||||||
|
1 => $this->contributor->username,
|
||||||
|
]);
|
||||||
|
return view('contributor/view', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add(): string
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
$users = (new UserModel())->findAll();
|
||||||
|
$contributorOptions = array_reduce(
|
||||||
|
$users,
|
||||||
|
static function ($result, $user) {
|
||||||
|
$result[$user->id] = $user->username;
|
||||||
|
return $result;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$roles = setting('AuthGroups.podcastBaseGroups');
|
||||||
|
$roleOptions = [];
|
||||||
|
array_walk(
|
||||||
|
$roles,
|
||||||
|
static function ($role, $key) use (&$roleOptions): array {
|
||||||
|
$roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title');
|
||||||
|
return $roleOptions;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'contributorOptions' => $contributorOptions,
|
||||||
|
'roleOptions' => $roleOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->at_handle,
|
||||||
|
]);
|
||||||
|
return view('contributor/add', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attemptAdd(): RedirectResponse
|
||||||
|
{
|
||||||
|
$user = (new UserModel())->find((int) $this->request->getPost('user'));
|
||||||
|
|
||||||
|
if (get_podcast_group($user, $this->podcast->id)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', [lang('Contributor.messages.alreadyAddedError')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_podcast_group($user, $this->podcast->id, $this->request->getPost('role'));
|
||||||
|
|
||||||
|
return redirect()->route('contributor-list', [$this->podcast->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(): string|RedirectResponse
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
$roles = setting('AuthGroups.podcastBaseGroups');
|
||||||
|
$roleOptions = [];
|
||||||
|
array_walk(
|
||||||
|
$roles,
|
||||||
|
static function ($role) use (&$roleOptions): array {
|
||||||
|
$roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title');
|
||||||
|
return $roleOptions;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$contributorGroup = get_podcast_group($this->contributor, $this->podcast->id);
|
||||||
|
|
||||||
|
if ($contributorGroup === null) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', [lang('Contributor.messages.notAddedError')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'contributor' => $this->contributor,
|
||||||
|
'contributorGroup' => $contributorGroup,
|
||||||
|
'roleOptions' => $roleOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->at_handle,
|
||||||
|
1 => $this->contributor->username,
|
||||||
|
]);
|
||||||
|
return view('contributor/edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attemptEdit(): RedirectResponse
|
||||||
|
{
|
||||||
|
// forbid updating a podcast owner
|
||||||
|
if ($this->podcast->created_by === $this->contributor->id) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('errors', [lang('Contributor.messages.editOwnerError')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$group = $this->request->getPost('role');
|
||||||
|
|
||||||
|
set_podcast_group($this->contributor, $this->podcast->id, $group);
|
||||||
|
|
||||||
|
cache()
|
||||||
|
->delete("podcast#{$this->podcast->id}_contributors");
|
||||||
|
|
||||||
|
return redirect()->route('contributor-list', [$this->podcast->id])->with(
|
||||||
|
'message',
|
||||||
|
lang('Contributor.messages.editSuccess')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(): string
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'contributor' => $this->contributor,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->at_handle,
|
||||||
|
1 => $this->contributor->username,
|
||||||
|
]);
|
||||||
|
return view('contributor/delete', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attemptRemove(): RedirectResponse
|
||||||
|
{
|
||||||
|
if ($this->podcast->created_by === $this->contributor->id) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('errors', [lang('Contributor.messages.removeOwnerError')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'understand' => 'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (! $this->validate($rules)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $this->validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
cache()
|
||||||
|
->delete("podcast#{$this->podcast->id}_contributors");
|
||||||
|
|
||||||
|
// remove contributor from podcast group
|
||||||
|
$this->contributor->removeGroup(get_podcast_group($this->contributor, $this->podcast->id));
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('contributor-list', [$this->podcast->id])
|
||||||
|
->with(
|
||||||
|
'message',
|
||||||
|
lang('Contributor.messages.removeSuccess', [
|
||||||
|
'username' => $this->contributor->username,
|
||||||
|
'podcastTitle' => $this->podcast->title,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\Controller;
|
||||||
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ActionController
|
||||||
|
*
|
||||||
|
* A generic controller to handle Authentication Actions.
|
||||||
|
*/
|
||||||
|
class InteractController extends Controller
|
||||||
|
{
|
||||||
|
public function attemptInteractAsActor(): RedirectResponse
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
'actor_id' => 'required|numeric',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (! $this->validate($rules)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', service('validation')->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
helper('auth');
|
||||||
|
|
||||||
|
set_interact_as_actor((int) $this->request->getPost('actor_id'));
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\Shield\Controllers\LoginController as ShieldLoginController;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ViewThemes\Theme;
|
||||||
|
|
||||||
|
class LoginController extends ShieldLoginController
|
||||||
|
{
|
||||||
|
public function initController(
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
LoggerInterface $logger
|
||||||
|
): void {
|
||||||
|
parent::initController($request, $response, $logger);
|
||||||
|
|
||||||
|
Theme::setTheme('auth');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\Shield\Controllers\MagicLinkController as ShieldMagicLinkController;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ViewThemes\Theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Magic Link" logins - an email-based no-password login protocol. This works much like password reset would,
|
||||||
|
* but Shield provides this in place of password reset. It can also be used on it's own without an email/password login
|
||||||
|
* strategy.
|
||||||
|
*/
|
||||||
|
class MagicLinkController extends ShieldMagicLinkController
|
||||||
|
{
|
||||||
|
public function initController(
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
LoggerInterface $logger
|
||||||
|
): void {
|
||||||
|
parent::initController($request, $response, $logger);
|
||||||
|
|
||||||
|
Theme::setTheme('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPasswordView(): string | RedirectResponse
|
||||||
|
{
|
||||||
|
if (! session('magicLogin')) {
|
||||||
|
return redirect()->to(config('Auth')->loginRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
return view(setting('Auth.views')['magic-link-set-password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPasswordAction(): RedirectResponse
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
'new_password' => 'required|strong_password',
|
||||||
|
];
|
||||||
|
|
||||||
|
$userModel = new UserModel();
|
||||||
|
if (! $this->validate($rules)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $userModel->errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new password to user
|
||||||
|
auth()
|
||||||
|
->user()
|
||||||
|
->password = $this->request->getPost('new_password');
|
||||||
|
|
||||||
|
if (! $userModel->update(auth()->user()->id, auth()->user())) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $userModel->errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove magic login session to reinstate normal check
|
||||||
|
if (session('magicLogin')) {
|
||||||
|
session()->removeTempdata('magicLogin');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return redirect()->to(config('Auth')->loginRedirect())
|
||||||
|
->with('message', lang('MyAccount.messages.passwordChangeSuccess'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,11 @@ declare(strict_types=1);
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Modules\Admin\Controllers;
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
use App\Models\UserModel;
|
|
||||||
use CodeIgniter\HTTP\RedirectResponse;
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
use Config\Services;
|
use Modules\Admin\Controllers\BaseController;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
class MyAccountController extends BaseController
|
class MyAccountController extends BaseController
|
||||||
{
|
{
|
||||||
|
@ -30,16 +30,12 @@ class MyAccountController extends BaseController
|
||||||
|
|
||||||
public function attemptChange(): RedirectResponse
|
public function attemptChange(): RedirectResponse
|
||||||
{
|
{
|
||||||
$auth = Services::authentication();
|
|
||||||
$userModel = new UserModel();
|
|
||||||
|
|
||||||
// Validate here first, since some things,
|
|
||||||
// like the password, can only be validated properly here.
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'password' => 'required',
|
'password' => 'required',
|
||||||
'new_password' => 'required|strong_password|differs[password]',
|
'new_password' => 'required|strong_password|differs[password]',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$userModel = new UserModel();
|
||||||
if (! $this->validate($rules)) {
|
if (! $this->validate($rules)) {
|
||||||
return redirect()
|
return redirect()
|
||||||
->back()
|
->back()
|
||||||
|
@ -47,23 +43,28 @@ class MyAccountController extends BaseController
|
||||||
->with('errors', $userModel->errors());
|
->with('errors', $userModel->errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check credentials with the old password if logged in without magic link
|
||||||
$credentials = [
|
$credentials = [
|
||||||
'email' => user()
|
'email' => auth()
|
||||||
|
->user()
|
||||||
->email,
|
->email,
|
||||||
'password' => $this->request->getPost('password'),
|
'password' => $this->request->getPost('password'),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (! $auth->validate($credentials)) {
|
$validCreds = auth()
|
||||||
return redirect()
|
->check($credentials);
|
||||||
->back()
|
|
||||||
->withInput()
|
if (! $validCreds->isOK()) {
|
||||||
|
return redirect()->back()
|
||||||
->with('error', lang('MyAccount.messages.wrongPasswordError'));
|
->with('error', lang('MyAccount.messages.wrongPasswordError'));
|
||||||
}
|
}
|
||||||
|
|
||||||
user()
|
// set new password to user
|
||||||
|
auth()
|
||||||
|
->user()
|
||||||
->password = $this->request->getPost('new_password');
|
->password = $this->request->getPost('new_password');
|
||||||
|
|
||||||
if (! $userModel->update(user_id(), user())) {
|
if (! $userModel->update(auth()->user()->id, auth()->user())) {
|
||||||
return redirect()
|
return redirect()
|
||||||
->back()
|
->back()
|
||||||
->withInput()
|
->withInput()
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\Shield\Controllers\RegisterController as ShieldRegisterController;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use ViewThemes\Theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RegisterController
|
||||||
|
*
|
||||||
|
* Handles displaying registration form, and handling actual registration flow.
|
||||||
|
*/
|
||||||
|
class RegisterController extends ShieldRegisterController
|
||||||
|
{
|
||||||
|
public function initController(
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
LoggerInterface $logger
|
||||||
|
): void {
|
||||||
|
parent::initController($request, $response, $logger);
|
||||||
|
|
||||||
|
Theme::setTheme('auth');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,276 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2022 Ad Aures
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Modules\Auth\Controllers;
|
||||||
|
|
||||||
|
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||||
|
use CodeIgniter\HTTP\RedirectResponse;
|
||||||
|
use CodeIgniter\I18n\Time;
|
||||||
|
use CodeIgniter\Shield\Authentication\Authenticators\Session;
|
||||||
|
use CodeIgniter\Shield\Entities\User;
|
||||||
|
use CodeIgniter\Shield\Exceptions\ValidationException;
|
||||||
|
use CodeIgniter\Shield\Models\UserIdentityModel;
|
||||||
|
use Modules\Admin\Controllers\BaseController;
|
||||||
|
use Modules\Auth\Models\UserModel;
|
||||||
|
|
||||||
|
class UserController extends BaseController
|
||||||
|
{
|
||||||
|
protected ?User $user;
|
||||||
|
|
||||||
|
public function _remap(string $method, string ...$params): mixed
|
||||||
|
{
|
||||||
|
if ($params === []) {
|
||||||
|
return $this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->user = (new UserModel())->find($params[0])) {
|
||||||
|
return $this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw PageNotFoundException::forPageNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'users' => (new UserModel())->findAll(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('user/list', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(): string
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'user' => $this->user,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->user->username,
|
||||||
|
]);
|
||||||
|
return view('user/view', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(): string
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
$roles = setting('AuthGroups.instanceGroups');
|
||||||
|
$roleOptions = [];
|
||||||
|
array_walk(
|
||||||
|
$roles,
|
||||||
|
static function ($role, $key) use (&$roleOptions): array {
|
||||||
|
$roleOptions[$key] = $role['title'];
|
||||||
|
return $roleOptions;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'roleOptions' => $roleOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('user/create', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the user with the provided username and email. The password is set as a random string and a magic link is
|
||||||
|
* sent to the user to allow them setting their password.
|
||||||
|
*/
|
||||||
|
public function attemptCreate(): RedirectResponse
|
||||||
|
{
|
||||||
|
helper('text');
|
||||||
|
|
||||||
|
$db = db_connect();
|
||||||
|
$db->transStart();
|
||||||
|
|
||||||
|
$userModel = new UserModel();
|
||||||
|
|
||||||
|
// Save the user
|
||||||
|
$email = $this->request->getPost('email');
|
||||||
|
$user = new User([
|
||||||
|
'username' => $this->request->getPost('username'),
|
||||||
|
'email' => $email,
|
||||||
|
// set a random password
|
||||||
|
// user will be prompted to change it on first magic link login.
|
||||||
|
'password' => random_string('alnum', 32),
|
||||||
|
]);
|
||||||
|
try {
|
||||||
|
$userModel->save($user);
|
||||||
|
} catch (ValidationException) {
|
||||||
|
return redirect()->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $userModel->errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $userModel->findById($userModel->getInsertID());
|
||||||
|
$user->addGroup($this->request->getPost('role'));
|
||||||
|
|
||||||
|
// **** SEND WELCOME LINK FOR FIRST LOGIN ****
|
||||||
|
|
||||||
|
/** @var UserIdentityModel $identityModel */
|
||||||
|
$identityModel = model(UserIdentityModel::class);
|
||||||
|
|
||||||
|
// Delete any previous magic-link identities
|
||||||
|
$identityModel->deleteIdentitiesByType($user, Session::ID_TYPE_MAGIC_LINK);
|
||||||
|
|
||||||
|
// Generate the code and save it as an identity
|
||||||
|
$token = random_string('crypto', 20);
|
||||||
|
|
||||||
|
$identityModel->insert([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'type' => Session::ID_TYPE_MAGIC_LINK,
|
||||||
|
'secret' => $token,
|
||||||
|
'expires' => Time::now()->addSeconds(setting('Auth.welcomeLinkLifetime'))->format('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Send the user an email with the code
|
||||||
|
$email = emailer()
|
||||||
|
->setFrom(setting('Email.fromEmail'), setting('Email.fromName') ?? '');
|
||||||
|
$email->setTo($user->email);
|
||||||
|
$email->setSubject(lang('Auth.welcomeSubject', [
|
||||||
|
'siteName' => setting('App.siteName'),
|
||||||
|
], null, false));
|
||||||
|
$email->setMessage(view(setting('Auth.views')['welcome-email'], [
|
||||||
|
'token' => $token,
|
||||||
|
], [
|
||||||
|
'theme' => 'auth',
|
||||||
|
]));
|
||||||
|
|
||||||
|
if (! $email->send(false)) {
|
||||||
|
log_message('error', $email->printDebugger(['headers']));
|
||||||
|
|
||||||
|
return redirect()->back()
|
||||||
|
->with('error', lang('Auth.unableSendEmailToUser', [$user->email]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the email
|
||||||
|
$email->clear();
|
||||||
|
|
||||||
|
$db->transComplete();
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return redirect()
|
||||||
|
->route('user-list')
|
||||||
|
->with('message', lang('User.messages.createSuccess', [
|
||||||
|
'username' => $user->username,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(): string
|
||||||
|
{
|
||||||
|
helper('form');
|
||||||
|
|
||||||
|
$roles = setting('AuthGroups.instanceGroups');
|
||||||
|
$roleOptions = [];
|
||||||
|
array_walk(
|
||||||
|
$roles,
|
||||||
|
static function ($role, $key) use (&$roleOptions): array {
|
||||||
|
$roleOptions[$key] = $role['title'];
|
||||||
|
return $roleOptions;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'user' => $this->user,
|
||||||
|
'roleOptions' => $roleOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->user->username,
|
||||||
|
]);
|
||||||
|
return view('user/edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attemptEdit(): RedirectResponse
|
||||||
|
{
|
||||||
|
// The instance owner is a superadmin and the only user that cannot be demoted.
|
||||||
|
if ((bool) $this->user->is_owner) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('errors', [
|
||||||
|
lang('User.messages.editOwnerError', [
|
||||||
|
'username' => $this->user->username,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$group = $this->request->getPost('role');
|
||||||
|
|
||||||
|
set_instance_group($this->user, $group);
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return redirect()
|
||||||
|
->route('user-list')
|
||||||
|
->with('message', lang('User.messages.roleEditSuccess', [
|
||||||
|
'username' => $this->user->username,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(): string
|
||||||
|
{
|
||||||
|
helper(['form']);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'user' => $this->user,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->user->username,
|
||||||
|
]);
|
||||||
|
return view('user/delete', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attemptDelete(): RedirectResponse
|
||||||
|
{
|
||||||
|
// You cannot delete the instance owner.
|
||||||
|
if ((bool) $this->user->is_owner) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('errors', [
|
||||||
|
lang('User.messages.deleteOwnerError', [
|
||||||
|
'username' => $this->user->username,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You cannot delete a superadmin
|
||||||
|
// superadmin has to be demoted before being deleted
|
||||||
|
if ($this->user->inGroup(setting('AuthGroups.mostPowerfulPodcastGroup'))) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('errors', [
|
||||||
|
lang('User.messages.deleteSuperAdminError', [
|
||||||
|
'username' => $this->user->username,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'understand' => 'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (! $this->validate($rules)) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('errors', $this->validator->getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
(new UserModel())->delete($this->user->id, true);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('user-list')
|
||||||
|
->with('message', lang('User.messages.deleteSuccess', [
|
||||||
|
'username' => $this->user->username,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AddPodcastUsers Creates podcast_users table in database
|
|
||||||
*
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Auth\Database\Migrations;
|
|
||||||
|
|
||||||
use CodeIgniter\Database\Migration;
|
|
||||||
|
|
||||||
class AddPodcastsUsers extends Migration
|
|
||||||
{
|
|
||||||
public function up(): void
|
|
||||||
{
|
|
||||||
$this->forge->addField([
|
|
||||||
'podcast_id' => [
|
|
||||||
'type' => 'INT',
|
|
||||||
'unsigned' => true,
|
|
||||||
],
|
|
||||||
'user_id' => [
|
|
||||||
'type' => 'INT',
|
|
||||||
'unsigned' => true,
|
|
||||||
],
|
|
||||||
'group_id' => [
|
|
||||||
'type' => 'INT',
|
|
||||||
'unsigned' => true,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
$this->forge->addPrimaryKey(['user_id', 'podcast_id']);
|
|
||||||
$this->forge->addForeignKey('user_id', 'users', 'id', '', 'CASCADE');
|
|
||||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id', '', 'CASCADE');
|
|
||||||
$this->forge->addForeignKey('group_id', 'auth_groups', 'id', '', 'CASCADE');
|
|
||||||
$this->forge->createTable('podcasts_users');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
$this->forge->dropTable('podcasts_users');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
// Add custom column for shield
|
||||||
|
class AddCustomColumnForUser extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$fields = [
|
||||||
|
'is_owner' => [
|
||||||
|
'type' => 'TINYINT',
|
||||||
|
'constraint' => 1,
|
||||||
|
'default' => 0,
|
||||||
|
'null' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->forge->addColumn('users', $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$fields = ['is_owner'];
|
||||||
|
$this->forge->dropColumn('users', $fields);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,302 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PermissionSeeder Inserts permissions
|
|
||||||
*
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Auth\Database\Seeds;
|
|
||||||
|
|
||||||
use CodeIgniter\Database\Seeder;
|
|
||||||
|
|
||||||
class AuthSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array<string, string>[]
|
|
||||||
*/
|
|
||||||
protected array $groups = [
|
|
||||||
[
|
|
||||||
'name' => 'superadmin',
|
|
||||||
'description' =>
|
|
||||||
'Somebody who has access to all the castopod instance features',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'podcast_admin',
|
|
||||||
'description' =>
|
|
||||||
'Somebody who has access to all the features within a given podcast',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build permissions array as a list of:
|
|
||||||
*
|
|
||||||
* ``` context => [ [action, description], [action, description], ... ] ```
|
|
||||||
*
|
|
||||||
* @var array<string, array<string, string|array>[]>
|
|
||||||
*/
|
|
||||||
protected array $permissions = [
|
|
||||||
'users' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Create a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all users',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any user info',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_authorizations',
|
|
||||||
'description' => 'Add or remove roles/permissions to a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_bans',
|
|
||||||
'description' => 'Ban / unban a user',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'force_pass_reset',
|
|
||||||
'description' =>
|
|
||||||
'Force a user to update his password upon next login',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete user without removing him from database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete_permanently',
|
|
||||||
'description' =>
|
|
||||||
'Delete all occurrences of a user from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'pages' => [
|
|
||||||
[
|
|
||||||
'name' => 'manage',
|
|
||||||
'description' => 'List / create / edit / delete pages',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcasts' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add a new podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'import',
|
|
||||||
'description' => 'Import a new podcast from an external feed',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all podcasts and their episodes',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any podcast and their contributors list',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' => 'Delete any podcast from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'episodes' => [
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all episodes of any podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any episode of any podcast',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcast' => [
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_contributors',
|
|
||||||
'description' =>
|
|
||||||
'Add / remove contributors to a podcast and edit their roles',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_platforms',
|
|
||||||
'description' => 'Set / remove platform links of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'manage_publications',
|
|
||||||
'description' =>
|
|
||||||
'Publish / unpublish episodes & posts of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'interact_as',
|
|
||||||
'description' =>
|
|
||||||
'Interact as the podcast to favourite / share or reply to posts.',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'podcast_episodes' => [
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all episodes of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any episode of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add new episodes for a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit an episode of a podcast',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete all occurrences of an episode of a podcast from the database',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'person' => [
|
|
||||||
[
|
|
||||||
'name' => 'create',
|
|
||||||
'description' => 'Add a new person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'list',
|
|
||||||
'description' => 'List all persons',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'view',
|
|
||||||
'description' => 'View any person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'edit',
|
|
||||||
'description' => 'Edit a person',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete',
|
|
||||||
'description' =>
|
|
||||||
'Delete permanently any person from the database',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'fediverse' => [
|
|
||||||
[
|
|
||||||
'name' => 'block_actors',
|
|
||||||
'description' =>
|
|
||||||
'Block fediverse actors from interacting with the instance.',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'block_domains',
|
|
||||||
'description' =>
|
|
||||||
'Block fediverse domains from interacting with the instance.',
|
|
||||||
'has_permission' => ['superadmin'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
$groupId = 0;
|
|
||||||
$dataGroups = [];
|
|
||||||
foreach ($this->groups as $group) {
|
|
||||||
$dataGroups[] = [
|
|
||||||
'id' => ++$groupId,
|
|
||||||
'name' => $group['name'],
|
|
||||||
'description' => $group['description'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map permissions to a format the `auth_permissions` table expects
|
|
||||||
$dataPermissions = [];
|
|
||||||
$dataGroupsPermissions = [];
|
|
||||||
$permissionId = 0;
|
|
||||||
foreach ($this->permissions as $context => $actions) {
|
|
||||||
foreach ($actions as $action) {
|
|
||||||
$dataPermissions[] = [
|
|
||||||
'id' => ++$permissionId,
|
|
||||||
'name' => $context . '-' . $action['name'],
|
|
||||||
'description' => $action['description'],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($action['has_permission'] as $role) {
|
|
||||||
// link permission to specified groups
|
|
||||||
$dataGroupsPermissions[] = [
|
|
||||||
'group_id' => $this->getGroupIdByName($role, $dataGroups),
|
|
||||||
'permission_id' => $permissionId,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->db
|
|
||||||
->table('auth_permissions')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataPermissions);
|
|
||||||
$this->db
|
|
||||||
->table('auth_groups')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataGroups);
|
|
||||||
$this->db
|
|
||||||
->table('auth_groups_permissions')
|
|
||||||
->ignore(true)
|
|
||||||
->insertBatch($dataGroupsPermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, string|int>[] $dataGroups
|
|
||||||
*/
|
|
||||||
public static function getGroupIdByName(string $name, array $dataGroups): ?int
|
|
||||||
{
|
|
||||||
foreach ($dataGroups as $group) {
|
|
||||||
if ($group['name'] === $name) {
|
|
||||||
return $group['id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2020 Ad Aures
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Modules\Auth\Entities;
|
|
||||||
|
|
||||||
use App\Entities\Podcast;
|
|
||||||
use App\Models\PodcastModel;
|
|
||||||
use App\Models\UserModel;
|
|
||||||
use Modules\Fediverse\Models\NotificationModel;
|
|
||||||
use Myth\Auth\Entities\User as MythAuthUser;
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $id
|
|
||||||
* @property string $username
|
|
||||||
* @property string $email
|
|
||||||
* @property string $password
|
|
||||||
* @property bool $active
|
|
||||||
* @property bool $force_pass_reset
|
|
||||||
* @property int|null $podcast_id
|
|
||||||
* @property string|null $podcast_role
|
|
||||||
*
|
|
||||||
* @property Podcast[] $podcasts All podcasts the user is contributing to
|
|
||||||
* @property int[] $actorIdsWithUnreadNotifications Ids of the user's actors that have unread notifications
|
|
||||||
*/
|
|
||||||
class User extends MythAuthUser
|
|
||||||
{
|
|
||||||
public bool $is_owner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Podcast[]|null
|
|
||||||
*/
|
|
||||||
protected ?array $podcasts = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int[]|null
|
|
||||||
*/
|
|
||||||
protected ?array $actorIdsWithUnreadNotifications = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of field names and the type of value to cast them as when they are accessed.
|
|
||||||
*
|
|
||||||
* @var array<string, string>
|
|
||||||
*/
|
|
||||||
protected $casts = [
|
|
||||||
'id' => 'integer',
|
|
||||||
'active' => 'boolean',
|
|
||||||
'force_pass_reset' => 'boolean',
|
|
||||||
'podcast_id' => '?integer',
|
|
||||||
'podcast_role' => '?string',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function getIsOwner(): bool
|
|
||||||
{
|
|
||||||
$firstUser = (new UserModel())->first();
|
|
||||||
|
|
||||||
if (! $firstUser instanceof self) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->username === $firstUser->username;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the podcasts the user is contributing to
|
|
||||||
*
|
|
||||||
* @return Podcast[]
|
|
||||||
*/
|
|
||||||
public function getPodcasts(): array
|
|
||||||
{
|
|
||||||
if ($this->id === null) {
|
|
||||||
throw new RuntimeException('Users must be created before getting podcasts.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->podcasts === null) {
|
|
||||||
$this->podcasts = (new PodcastModel())->getUserPodcasts($this->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->podcasts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ids of the user's actors that have unread notifications
|
|
||||||
*
|
|
||||||
* @return int[]
|
|
||||||
*/
|
|
||||||
public function getActorIdsWithUnreadNotifications(): array
|
|
||||||
{
|
|
||||||
if ($this->getPodcasts() === []) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$unreadNotifications = (new NotificationModel())->whereIn(
|
|
||||||
'target_actor_id',
|
|
||||||
array_column($this->getPodcasts(), 'actor_id')
|
|
||||||
)
|
|
||||||
->where('read_at', null)
|
|
||||||
->findAll();
|
|
||||||
|
|
||||||
return array_column($unreadNotifications, 'target_actor_id');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,8 +8,8 @@ use App\Models\PodcastModel;
|
||||||
use CodeIgniter\Filters\FilterInterface;
|
use CodeIgniter\Filters\FilterInterface;
|
||||||
use CodeIgniter\HTTP\RequestInterface;
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\Shield\Exceptions\RuntimeException;
|
||||||
use Config\Services;
|
use Config\Services;
|
||||||
use Myth\Auth\Exceptions\PermissionException;
|
|
||||||
|
|
||||||
class PermissionFilter implements FilterInterface
|
class PermissionFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
|
@ -24,62 +24,49 @@ class PermissionFilter implements FilterInterface
|
||||||
*/
|
*/
|
||||||
public function before(RequestInterface $request, $params = null)
|
public function before(RequestInterface $request, $params = null)
|
||||||
{
|
{
|
||||||
helper('auth');
|
if (empty($params)) {
|
||||||
|
|
||||||
if ($params === null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$authenticate = Services::authentication();
|
if (! function_exists('auth')) {
|
||||||
|
helper('auth');
|
||||||
// if no user is logged in then send to the login form
|
|
||||||
if (! $authenticate->check()) {
|
|
||||||
session()->set('redirect_url', current_url());
|
|
||||||
return redirect('login');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
helper('misc');
|
if (! auth()->loggedIn()) {
|
||||||
$authorize = Services::authorization();
|
return redirect()->to('login');
|
||||||
$router = Services::router();
|
}
|
||||||
$routerParams = $router->params();
|
|
||||||
$result = false;
|
$result = true;
|
||||||
|
|
||||||
// Check if user has at least one of the permissions
|
|
||||||
foreach ($params as $permission) {
|
foreach ($params as $permission) {
|
||||||
// check if permission is for a specific podcast
|
// does permission is specific to a podcast?
|
||||||
if (
|
if (str_contains($permission, '#')) {
|
||||||
(str_starts_with($permission, 'podcast-') ||
|
$router = Services::router();
|
||||||
str_starts_with($permission, 'podcast_episodes-')) &&
|
$routerParams = $router->params();
|
||||||
$routerParams !== []
|
|
||||||
) {
|
// get podcast id
|
||||||
if (
|
$podcastId = null;
|
||||||
($groupId = (new PodcastModel())->getContributorGroupId(
|
if (is_numeric($routerParams[0])) {
|
||||||
$authenticate->id(),
|
$podcastId = (int) $routerParams[0];
|
||||||
$routerParams[0],
|
} else {
|
||||||
)) &&
|
$podcast = (new PodcastModel())->getPodcastByHandle($routerParams[0]);
|
||||||
$authorize->groupHasPermission($permission, $groupId)
|
if ($podcast !== null) {
|
||||||
) {
|
$podcastId = $podcast->id;
|
||||||
$result = true;
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
if ($podcastId !== null) {
|
||||||
|
$permission = str_replace('#', '#' . $podcastId, $permission);
|
||||||
}
|
}
|
||||||
} elseif (
|
|
||||||
$authorize->hasPermission($permission, $authenticate->id())
|
|
||||||
) {
|
|
||||||
$result = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$result = $result && auth()
|
||||||
|
->user()
|
||||||
|
->can($permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $result) {
|
if (! $result) {
|
||||||
if ($authenticate->silent()) {
|
throw new RuntimeException(lang('Auth.notEnoughPrivilege'), 403);
|
||||||
$redirectURL = session('redirect_url') ?? '/';
|
|
||||||
unset($_SESSION['redirect_url']);
|
|
||||||
return redirect()
|
|
||||||
->to($redirectURL)
|
|
||||||
->with('error', lang('Auth.notEnoughPrivilege'));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new PermissionException(lang('Auth.notEnoughPrivilege'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Ad Aures
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Entities\Podcast;
|
||||||
|
use App\Models\ActorModel;
|
||||||
|
use App\Models\PodcastModel;
|
||||||
|
use CodeIgniter\Shield\Entities\User;
|
||||||
|
use Modules\Auth\Auth;
|
||||||
|
use Modules\Fediverse\Entities\Actor;
|
||||||
|
use Modules\Fediverse\Models\NotificationModel;
|
||||||
|
|
||||||
|
if (! function_exists('auth')) {
|
||||||
|
/**
|
||||||
|
* Provides convenient access to the main Auth class for CodeIgniter Shield.
|
||||||
|
*
|
||||||
|
* @param string|null $alias Authenticator alias
|
||||||
|
*/
|
||||||
|
function auth(?string $alias = null): Auth
|
||||||
|
{
|
||||||
|
/** @var Auth $auth */
|
||||||
|
$auth = service('auth');
|
||||||
|
|
||||||
|
return $auth->setAuthenticator($alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('set_interact_as_actor')) {
|
||||||
|
/**
|
||||||
|
* Sets the actor id of which the user is acting as
|
||||||
|
*/
|
||||||
|
function set_interact_as_actor(int $actorId): void
|
||||||
|
{
|
||||||
|
if (auth()->loggedIn()) {
|
||||||
|
session()
|
||||||
|
->set('interact_as_actor_id', $actorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('remove_interact_as_actor')) {
|
||||||
|
/**
|
||||||
|
* Removes the actor id of which the user is acting as
|
||||||
|
*/
|
||||||
|
function remove_interact_as_actor(): void
|
||||||
|
{
|
||||||
|
session()->remove('interact_as_actor_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('interact_as_actor_id')) {
|
||||||
|
/**
|
||||||
|
* Sets the podcast id of which the user is acting as
|
||||||
|
*/
|
||||||
|
function interact_as_actor_id(): ?int
|
||||||
|
{
|
||||||
|
return session()->get('interact_as_actor_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('interact_as_actor')) {
|
||||||
|
/**
|
||||||
|
* Get the actor the user is currently interacting as
|
||||||
|
*/
|
||||||
|
function interact_as_actor(): Actor | false
|
||||||
|
{
|
||||||
|
if (! auth()->loggedIn()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$session = session();
|
||||||
|
if (! $session->has('interact_as_actor_id')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return model(ActorModel::class, false)->getActorById($session->get('interact_as_actor_id'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('can_user_interact')) {
|
||||||
|
function can_user_interact(): bool
|
||||||
|
{
|
||||||
|
return (bool) interact_as_actor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('add_podcast_group')) {
|
||||||
|
function add_podcast_group(User $user, int $podcastId, string $group): User
|
||||||
|
{
|
||||||
|
$podcastGroup = 'podcast#' . $podcastId . '-' . $group;
|
||||||
|
|
||||||
|
return $user->addGroup($podcastGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_instance_group')) {
|
||||||
|
function get_instance_group(User $user): ?string
|
||||||
|
{
|
||||||
|
$instanceGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
|
||||||
|
return ! str_starts_with($group, 'podcast#');
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($instanceGroups === []) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$instanceGroup = array_shift($instanceGroups);
|
||||||
|
|
||||||
|
// Verify that a user belongs to one group only!
|
||||||
|
if ($instanceGroups !== []) {
|
||||||
|
// remove any other group the user belongs to
|
||||||
|
$user->removeGroup(...$instanceGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instanceGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('set_instance_group')) {
|
||||||
|
function set_instance_group(User $user, string $group): User
|
||||||
|
{
|
||||||
|
// remove old instance group
|
||||||
|
if (get_instance_group($user)) {
|
||||||
|
$user->removeGroup(get_instance_group($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new group
|
||||||
|
return $user->addGroup($group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_podcast_group')) {
|
||||||
|
function get_podcast_group(User $user, int $podcastId): ?string
|
||||||
|
{
|
||||||
|
$podcastGroups = array_filter($user->getGroups() ?? [], static function ($group) use ($podcastId): bool {
|
||||||
|
return str_starts_with($group, "podcast#{$podcastId}");
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($podcastGroups === []) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$podcastGroup = array_shift($podcastGroups);
|
||||||
|
|
||||||
|
// Verify that a user belongs to one group only!
|
||||||
|
if ($podcastGroups !== []) {
|
||||||
|
// remove any other group the user belongs to
|
||||||
|
$user->removeGroup(...$podcastGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip the `podcast#{id}.` prefix when returning group
|
||||||
|
return substr($podcastGroup, strlen('podcast#' . $podcastId . '-'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('set_podcast_group')) {
|
||||||
|
function set_podcast_group(User $user, int $podcastId, string $group): User
|
||||||
|
{
|
||||||
|
// remove old instance group
|
||||||
|
$user->removeGroup("podcast#{$podcastId}-" . get_podcast_group($user, $podcastId));
|
||||||
|
|
||||||
|
// set new group
|
||||||
|
return add_podcast_group($user, $podcastId, $group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_podcast_groups')) {
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
function get_user_podcast_ids(User $user): array
|
||||||
|
{
|
||||||
|
$podcastGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
|
||||||
|
return str_starts_with($group, 'podcast#');
|
||||||
|
});
|
||||||
|
|
||||||
|
$userPodcastIds = [];
|
||||||
|
// extract all podcast ids from groups
|
||||||
|
foreach ($podcastGroups as $podcastGroup) {
|
||||||
|
$userPodcastIds[] = substr($podcastGroup, strpos($podcastGroup, '#') + 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $userPodcastIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('can_podcast')) {
|
||||||
|
function can_podcast(User $user, int $podcastId, string $permission): bool
|
||||||
|
{
|
||||||
|
return $user->can('podcast#' . $podcastId . '.' . $permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_user_podcasts')) {
|
||||||
|
/**
|
||||||
|
* Returns the podcasts the user is contributing to
|
||||||
|
*
|
||||||
|
* @return Podcast[]
|
||||||
|
*/
|
||||||
|
function get_user_podcasts(User $user): array
|
||||||
|
{
|
||||||
|
return (new PodcastModel())->getUserPodcasts($user->id, get_user_podcast_ids($user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_podcasts_user_can_interact_with')) {
|
||||||
|
/**
|
||||||
|
* @return Podcast[]
|
||||||
|
*/
|
||||||
|
function get_podcasts_user_can_interact_with(User $user): array
|
||||||
|
{
|
||||||
|
$userPodcasts = (new PodcastModel())->getUserPodcasts($user->id, get_user_podcast_ids($user));
|
||||||
|
|
||||||
|
$hasInteractAsPrivilege = interact_as_actor_id() === null;
|
||||||
|
|
||||||
|
if ($userPodcasts === []) {
|
||||||
|
if ($hasInteractAsPrivilege) {
|
||||||
|
remove_interact_as_actor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$isInteractAsPrivilegeLost = true;
|
||||||
|
$podcastsUserCanInteractWith = [];
|
||||||
|
foreach ($userPodcasts as $userPodcast) {
|
||||||
|
if (can_podcast($user, $userPodcast->id, 'interact-as')) {
|
||||||
|
if (interact_as_actor_id() === $userPodcast->actor_id) {
|
||||||
|
$isInteractAsPrivilegeLost = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$podcastsUserCanInteractWith[] = $userPodcast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($podcastsUserCanInteractWith === []) {
|
||||||
|
if (interact_as_actor_id() !== null) {
|
||||||
|
remove_interact_as_actor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if user has lost the interact as privilege for current podcast actor.
|
||||||
|
// --> Remove interact as if there's no podcast actor to interact as
|
||||||
|
// or set the first podcast actor the user can interact as
|
||||||
|
if ($isInteractAsPrivilegeLost) {
|
||||||
|
set_interact_as_actor($podcastsUserCanInteractWith[0]->actor_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $podcastsUserCanInteractWith;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_actor_ids_with_unread_notifications')) {
|
||||||
|
/**
|
||||||
|
* Returns the ids of the user's actors that have unread notifications
|
||||||
|
*
|
||||||
|
* @return int[]
|
||||||
|
*/
|
||||||
|
function get_actor_ids_with_unread_notifications(User $user): array
|
||||||
|
{
|
||||||
|
if (($userPodcasts = get_user_podcasts($user)) === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$unreadNotifications = (new NotificationModel())->whereIn(
|
||||||
|
'target_actor_id',
|
||||||
|
array_column($userPodcasts, 'actor_id')
|
||||||
|
)
|
||||||
|
->where('read_at', null)
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
return array_column($unreadNotifications, 'target_actor_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('get_group_title')) {
|
||||||
|
/**
|
||||||
|
* @return array<'title'|'description', string>
|
||||||
|
*/
|
||||||
|
function get_group_info(string $group, ?int $podcastId = null): array
|
||||||
|
{
|
||||||
|
if ($podcastId === null) {
|
||||||
|
return setting('AuthGroups.instanceGroups')[$group];
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting('AuthGroups.podcastGroups')[$group];
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue