Skip to content
Snippets Groups Projects
Commit c63a0776 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(users): add myth-auth to handle users crud + add admin gateway only accessible by login

- overwrite myth/auth config with castopod app needs
- create custom views for users authentication
- add admin area bootstrapped by admin controller
- shift podcast and episodes crud to admin area
- reorganize view layouts
- update docs for database migration
- add myth-auth to DEPENDENCIES.md

closes #11
parent da0f0472
No related branches found
No related tags found
No related merge requests found
Showing
with 945 additions and 296 deletions
......@@ -13,3 +13,4 @@ Castopod uses the following components:
- [GeoIP2 PHP API](https://github.com/maxmind/GeoIP2-php) ([Apache License 2.0](https://github.com/maxmind/GeoIP2-php/blob/master/LICENSE))
- [Quill Rich Text Editor](https://github.com/quilljs/quill) ([BSD 3-Clause "New" or "Revised" License](https://github.com/quilljs/quill/blob/develop/LICENSE))
- [getID3](https://github.com/JamesHeinrich/getID3) ([GNU General Public License v3](https://github.com/JamesHeinrich/getID3/blob/2.0/licenses/license.gpl-30.txt))
- [myth-auth](https://github.com/lonnieezell/myth-auth) ([MIT license](https://github.com/lonnieezell/myth-auth/blob/develop/LICENSE.md))
......@@ -274,4 +274,20 @@ class App extends BaseConfig
| Defines the root folder for media files storage
*/
public $mediaRoot = 'media';
/*
|--------------------------------------------------------------------------
| Admin gateway
|--------------------------------------------------------------------------
| Defines a base route for all admin pages
*/
public $adminGateway = 'admin';
/*
|--------------------------------------------------------------------------
| Auth gateway
|--------------------------------------------------------------------------
| Defines a base route for all authentication related pages
*/
public $authGateway = 'auth';
}
<?php
namespace Config;
class Auth extends \Myth\Auth\Config\Auth
{
//--------------------------------------------------------------------
// Views used by Auth Controllers
//--------------------------------------------------------------------
public $views = [
'login' => 'auth/login',
'register' => 'auth/register',
'forgot' => 'auth/forgot',
'reset' => 'auth/reset',
'emailForgot' => 'auth/emails/forgot',
'emailActivation' => 'auth/emails/activation',
];
//--------------------------------------------------------------------
// Layout for the views to extend
//--------------------------------------------------------------------
public $viewLayout = 'auth/_layout';
//--------------------------------------------------------------------
// Allow User Registration
//--------------------------------------------------------------------
// When enabled (default) any unregistered user may apply for a new
// account. If you disable registration you may need to ensure your
// controllers and views know not to offer registration.
//
public $allowRegistration = false;
//--------------------------------------------------------------------
// Require confirmation registration via email
//--------------------------------------------------------------------
// When enabled, every registered user will receive an email message
// with a special link he have to confirm to activate his account.
//
public $requireActivation = false;
}
......@@ -10,6 +10,9 @@ class Filters extends BaseConfig
'csrf' => \CodeIgniter\Filters\CSRF::class,
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
'login' => \Myth\Auth\Filters\LoginFilter::class,
'role' => \Myth\Auth\Filters\RoleFilter::class,
'permission' => \Myth\Auth\Filters\PermissionFilter::class,
];
// Always applied before every request
......@@ -33,4 +36,13 @@ class Filters extends BaseConfig
// that they should run on, like:
// 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']],
public $filters = [];
public function __construct()
{
parent::__construct();
$this->filters = [
'login' => ['before' => [config('App')->adminGateway . '*']],
];
}
}
......@@ -24,6 +24,7 @@ $routes->set404Override();
$routes->setAutoRoute(false);
$routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,191}');
$routes->addPlaceholder('episodeSlug', '[a-zA-Z0-9\-]{1,191}');
$routes->addPlaceholder('username', '[a-zA-Z0-9 ]{3,}');
/**
* --------------------------------------------------------------------
......@@ -34,28 +35,13 @@ $routes->addPlaceholder('episodeSlug', '[a-zA-Z0-9\-]{1,191}');
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index', ['as' => 'home']);
$routes->add('new-podcast', 'Podcast::create', ['as' => 'podcast_create']);
$routes->group('@(:podcastName)', function ($routes) {
$routes->add('/', 'Podcast::view/$1', ['as' => 'podcast_view']);
$routes->add('edit', 'Podcast::edit/$1', [
'as' => 'podcast_edit',
]);
$routes->add('delete', 'Podcast::delete/$1', [
'as' => 'podcast_delete',
]);
$routes->add('/', 'Podcast/$1', ['as' => 'podcast']);
$routes->add('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
$routes->add('new-episode', 'Episode::create/$1', [
'as' => 'episode_create',
]);
$routes->add('episodes/(:episodeSlug)', 'Episode::view/$1/$2', [
'as' => 'episode_view',
]);
$routes->add('episodes/(:episodeSlug)/edit', 'Episode::edit/$1/$2', [
'as' => 'episode_edit',
]);
$routes->add('episodes/(:episodeSlug)/delete', 'Episode::delete/$1/$2', [
'as' => 'episode_delete',
$routes->add('episodes/(:episodeSlug)', 'Episode/$1/$2', [
'as' => 'episode',
]);
});
......@@ -68,6 +54,132 @@ $routes->add('stats/(:num)/(:num)/(:any)', 'Analytics::hit/$1/$2/$3', [
$routes->add('.well-known/unknown-useragents', 'UnknownUserAgents');
$routes->add('.well-known/unknown-useragents/(:num)', 'UnknownUserAgents/$1');
// Admin area
$routes->group(
config('App')->adminGateway,
['namespace' => 'App\Controllers\Admin'],
function ($routes) {
$routes->add('/', 'Home', [
'as' => 'admin',
]);
$routes->add('new-podcast', 'Podcast::create', [
'as' => 'podcast_create',
]);
$routes->add('podcasts', 'Podcast::list', ['as' => 'podcast_list']);
$routes->group('podcasts/@(:podcastName)', function ($routes) {
$routes->add('edit', 'Podcast::edit/$1', [
'as' => 'podcast_edit',
]);
$routes->add('delete', 'Podcast::delete/$1', [
'as' => 'podcast_delete',
]);
$routes->add('new-episode', 'Episode::create/$1', [
'as' => 'episode_create',
]);
$routes->add('episodes', 'Episode::list/$1', [
'as' => 'episode_list',
]);
$routes->add(
'episodes/(:episodeSlug)/edit',
'Episode::edit/$1/$2',
[
'as' => 'episode_edit',
]
);
$routes->add(
'episodes/(:episodeSlug)/delete',
'Episode::delete/$1/$2',
[
'as' => 'episode_delete',
]
);
});
// Users
$routes->add('users', 'User::list', ['as' => 'user_list']);
$routes->add('new-user', 'User::create', ['as' => 'user_create']);
$routes->add('users/@(:any)/ban', 'User::ban/$1', [
'as' => 'user_ban',
]);
$routes->add('users/@(:any)/unban', 'User::unBan/$1', [
'as' => 'user_unban',
]);
$routes->add(
'users/@(:any)/force-pass-reset',
'User::forcePassReset/$1',
[
'as' => 'user_force_pass_reset',
]
);
$routes->add('users/@(:any)/delete', 'User::delete/$1', [
'as' => 'user_delete',
]);
// My account
$routes->get('my-account', 'Myaccount', [
'as' => 'myAccount',
]);
$routes->get(
'my-account/change-password',
'Myaccount::changePassword/$1',
[
'as' => 'myAccount_change-password',
]
);
$routes->post(
'my-account/change-password',
'Myaccount::attemptChange/$1',
[
'as' => 'myAccount_change-password',
]
);
}
);
/**
* Overwriting Myth:auth routes file
*/
$routes->group(config('App')->authGateway, function ($routes) {
// Login/out
$routes->get('login', 'Auth::login', ['as' => 'login']);
$routes->post('login', 'Auth::attemptLogin');
$routes->get('logout', 'Auth::logout', ['as' => 'logout']);
// Registration
$routes->get('register', 'Auth::register', [
'as' => 'register',
]);
$routes->post('register', 'Auth::attemptRegister');
// Activation
$routes->get('activate-account', 'Auth::activateAccount', [
'as' => 'activate-account',
]);
$routes->get('resend-activate-account', 'Auth::resendActivateAccount', [
'as' => 'resend-activate-account',
]);
// Forgot/Resets
$routes->get('forgot', 'Auth::forgotPassword', [
'as' => 'forgot',
]);
$routes->post('forgot', 'Auth::attemptForgot');
$routes->get('reset-password', 'Auth::resetPassword', [
'as' => 'reset-password',
]);
$routes->post('reset-password', 'Auth::attemptReset');
$routes->get('change-password', 'Auth::changePassword', [
'as' => 'change_pass',
]);
$routes->post('change-password', 'Auth::attemptChange');
});
/**
* --------------------------------------------------------------------
* Additional Routing
......
......@@ -25,6 +25,7 @@ class Toolbar extends BaseConfig
\CodeIgniter\Debug\Toolbar\Collectors\Files::class,
\CodeIgniter\Debug\Toolbar\Collectors\Routes::class,
\CodeIgniter\Debug\Toolbar\Collectors\Events::class,
\Myth\Auth\Collectors\Auth::class,
];
/*
......
......@@ -17,6 +17,7 @@ class Validation
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
\Myth\Auth\Authentication\Passwords\ValidationRules::class,
];
/**
......
<?php
namespace App\Controllers\Admin;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components
* and performing functions that are needed by all your controllers.
* Extend this class in any new controllers:
* class Home extends BaseController
*
* For security be sure to declare any new methods as protected or private.
*
* @package CodeIgniter
*/
use CodeIgniter\Controller;
class BaseController extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* @var array
*/
protected $helpers = ['auth'];
/**
* Constructor.
*/
public function initController(
\CodeIgniter\HTTP\RequestInterface $request,
\CodeIgniter\HTTP\ResponseInterface $response,
\Psr\Log\LoggerInterface $logger
) {
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.:
// $this->session = \Config\Services::session();
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
class Episode extends BaseController
{
protected \App\Entities\Podcast $podcast;
protected ?\App\Entities\Episode $episode;
public function _remap($method, ...$params)
{
$podcast_model = new PodcastModel();
$this->podcast = $podcast_model->where('name', $params[0])->first();
if (count($params) > 1) {
$episode_model = new EpisodeModel();
if (
!($episode = $episode_model
->where([
'podcast_id' => $this->podcast->id,
'slug' => $params[1],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
}
return $this->$method();
}
public function list()
{
$episode_model = new EpisodeModel();
$data = [
'podcast' => $this->podcast,
'all_podcast_episodes' => $episode_model
->where('podcast_id', $this->podcast->id)
->find(),
];
return view('admin/episode/list', $data);
}
public function create()
{
helper(['form']);
if (
!$this->validate([
'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required|regex_match[[a-zA-Z0-9\-]{1,191}]',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
];
echo view('admin/episode/create', $data);
} else {
$new_episode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getVar('title'),
'slug' => $this->request->getVar('slug'),
'enclosure' => $this->request->getFile('enclosure'),
'pub_date' => $this->request->getVar('pub_date'),
'description' => $this->request->getVar('description'),
'image' => $this->request->getFile('image'),
'explicit' => $this->request->getVar('explicit') or false,
'number' => $this->request->getVar('episode_number'),
'season_number' => $this->request->getVar('season_number'),
'type' => $this->request->getVar('type'),
'author_name' => $this->request->getVar('author_name'),
'author_email' => $this->request->getVar('author_email'),
'block' => $this->request->getVar('block') or false,
]);
$episode_model = new EpisodeModel();
$episode_model->save($new_episode);
return redirect()->route('episode_list', [$this->podcast->name]);
}
}
public function edit()
{
helper(['form']);
if (
!$this->validate([
'enclosure' =>
'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required|regex_match[[a-zA-Z0-9\-]{1,191}]',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
echo view('admin/episode/edit', $data);
} else {
$this->episode->title = $this->request->getVar('title');
$this->episode->slug = $this->request->getVar('slug');
$this->episode->pub_date = $this->request->getVar('pub_date');
$this->episode->description = $this->request->getVar('description');
$this->episode->explicit =
($this->request->getVar('explicit') or false);
$this->episode->number = $this->request->getVar('episode_number');
$this->episode->season_number = $this->request->getVar(
'season_number'
)
? $this->request->getVar('season_number')
: null;
$this->episode->type = $this->request->getVar('type');
$this->episode->author_name = $this->request->getVar('author_name');
$this->episode->author_email = $this->request->getVar(
'author_email'
);
$this->episode->block = ($this->request->getVar('block') or false);
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $this->request->getFile(
'enclosure'
);
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $this->request->getFile('image');
}
$episode_model = new EpisodeModel();
$episode_model->save($this->episode);
return redirect()->route('episode_list', [$this->podcast->name]);
}
}
public function delete()
{
$episode_model = new EpisodeModel();
$episode_model->delete($this->episode->id);
return redirect()->route('episode_list', [$this->podcast->name]);
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
class Home extends BaseController
{
public function index()
{
return view('admin/dashboard');
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use Myth\Auth\Config\Services;
use Myth\Auth\Models\UserModel;
class Myaccount extends BaseController
{
public function index()
{
return view('admin/my_account/view');
}
public function changePassword()
{
return view('admin/my_account/change_password');
}
public function attemptChange()
{
$auth = Services::authentication();
$user_model = new UserModel();
// Validate here first, since some things,
// like the password, can only be validated properly here.
$rules = [
'email' => 'required|valid_email',
'password' => 'required',
'new_password' => 'required|strong_password',
'new_pass_confirm' => 'required|matches[new_password]',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $user_model->errors());
}
$credentials = [
'email' => user()->email,
'password' => $this->request->getPost('password'),
];
if (!$auth->validate($credentials)) {
return redirect()
->back()
->withInput()
->with('errors', $user_model->errors());
}
user()->password = $this->request->getPost('new_password');
$user_model->save(user());
if (!$user_model->save(user())) {
return redirect()
->back()
->withInput()
->with('errors', $user_model->errors());
}
// Success!
return redirect()
->route('myAccount')
->with('message', lang('MyAccount.passwordChangeSuccess'));
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use App\Entities\UserPodcast;
use App\Models\CategoryModel;
use App\Models\LanguageModel;
use App\Models\PodcastModel;
class Podcast extends BaseController
{
protected ?\App\Entities\Podcast $podcast;
public function _remap($method, ...$params)
{
if (count($params) > 0) {
$podcast_model = new PodcastModel();
if (
!($podcast = $podcast_model->where('name', $params[0])->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$this->podcast = $podcast;
}
return $this->$method();
}
public function list()
{
$podcast_model = new PodcastModel();
$data = ['all_podcasts' => $podcast_model->findAll()];
return view('admin/podcast/list', $data);
}
public function create()
{
helper(['form', 'misc']);
$podcast_model = new PodcastModel();
if (
!$this->validate([
'title' => 'required',
'name' => 'required|regex_match[[a-zA-Z0-9\_]{1,191}]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
'browser_lang' => get_browser_language(
$this->request->getServer('HTTP_ACCEPT_LANGUAGE')
),
];
echo view('admin/podcast/create', $data);
} else {
$podcast = new \App\Entities\Podcast([
'title' => $this->request->getVar('title'),
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description'),
'episode_description_footer' => $this->request->getVar(
'episode_description_footer'
),
'image' => $this->request->getFile('image'),
'language' => $this->request->getVar('language'),
'category' => $this->request->getVar('category'),
'explicit' => $this->request->getVar('explicit') or false,
'author_name' => $this->request->getVar('author_name'),
'author_email' => $this->request->getVar('author_email'),
'owner_name' => $this->request->getVar('owner_name'),
'owner_email' => $this->request->getVar('owner_email'),
'type' => $this->request->getVar('type'),
'copyright' => $this->request->getVar('copyright'),
'block' => $this->request->getVar('block') or false,
'complete' => $this->request->getVar('complete') or false,
'custom_html_head' => $this->request->getVar(
'custom_html_head'
),
]);
$db = \Config\Database::connect();
$db->transStart();
$new_podcast_id = $podcast_model->insert($podcast, true);
$user_podcast_model = new \App\Models\UserPodcastModel();
$user_podcast_model->save([
'user_id' => user()->id,
'podcast_id' => $new_podcast_id,
]);
$db->transComplete();
return redirect()->route('podcast_list', [$podcast->name]);
}
}
public function edit()
{
helper(['form', 'misc']);
if (
!$this->validate([
'title' => 'required',
'name' => 'required|regex_match[[a-zA-Z0-9\_]{1,191}]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'podcast' => $this->podcast,
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
];
echo view('admin/podcast/edit', $data);
} else {
$this->podcast->title = $this->request->getVar('title');
$this->podcast->name = $this->request->getVar('name');
$this->podcast->description = $this->request->getVar('description');
$this->podcast->episode_description_footer = $this->request->getVar(
'episode_description_footer'
);
$image = $this->request->getFile('image');
if ($image->isValid()) {
$this->podcast->image = $this->request->getFile('image');
}
$this->podcast->language = $this->request->getVar('language');
$this->podcast->category = $this->request->getVar('category');
$this->podcast->explicit =
($this->request->getVar('explicit') or false);
$this->podcast->author_name = $this->request->getVar('author_name');
$this->podcast->author_email = $this->request->getVar(
'author_email'
);
$this->podcast->owner_name = $this->request->getVar('owner_name');
$this->podcast->owner_email = $this->request->getVar('owner_email');
$this->podcast->type = $this->request->getVar('type');
$this->podcast->copyright = $this->request->getVar('copyright');
$this->podcast->block = ($this->request->getVar('block') or false);
$this->podcast->complete =
($this->request->getVar('complete') or false);
$this->podcast->custom_html_head = $this->request->getVar(
'custom_html_head'
);
$podcast_model = new PodcastModel();
$podcast_model->save($this->podcast);
return redirect()->route('podcast_list', [$this->podcast->name]);
}
}
public function delete()
{
$podcast_model = new PodcastModel();
$podcast_model->delete($this->podcast->id);
return redirect()->route('podcast_list');
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use Myth\Auth\Models\UserModel;
class User extends BaseController
{
protected ?\Myth\Auth\Entities\User $user;
public function _remap($method, ...$params)
{
if (count($params) > 0) {
$user_model = new UserModel();
if (
!($user = $user_model->where('username', $params[0])->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$this->user = $user;
}
return $this->$method();
}
public function list()
{
$user_model = new UserModel();
$data = ['all_users' => $user_model->findAll()];
return view('admin/user/list', $data);
}
public function create()
{
$user_model = new UserModel();
// Validate here first, since some things,
// like the password, can only be validated properly here.
$rules = array_merge(
$user_model->getValidationRules(['only' => ['username']]),
[
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|strong_password',
'pass_confirm' => 'required|matches[password]',
]
);
if (!$this->validate($rules)) {
echo view('admin/user/create');
} else {
// Save the user
$user = new \Myth\Auth\Entities\User($this->request->getPost());
// Activate user
$user->activate();
// Force user to reset his password on first connection
$user->force_pass_reset = true;
$user->generateResetHash();
if (!$user_model->save($user)) {
return redirect()
->back()
->withInput()
->with('errors', $user_model->errors());
}
// Success!
return redirect()
->route('user_list')
->with('message', lang('User.createSuccess'));
}
}
public function forcePassReset()
{
$user_model = new UserModel();
$this->user->force_pass_reset = true;
$this->user->generateResetHash();
if (!$user_model->save($this->user)) {
return redirect()
->back()
->with('errors', $user_model->errors());
}
// Success!
return redirect()
->route('user_list')
->with('message', lang('User.forcePassResetSuccess'));
}
public function ban()
{
$user_model = new UserModel();
$this->user->ban('');
if (!$user_model->save($this->user)) {
return redirect()
->back()
->with('errors', $user_model->errors());
}
return redirect()
->route('user_list')
->with('message', lang('User.banSuccess'));
}
public function unBan()
{
$user_model = new UserModel();
$this->user->unBan();
if (!$user_model->save($this->user)) {
return redirect()
->back()
->with('errors', $user_model->errors());
}
return redirect()
->route('user_list')
->with('message', lang('User.unbanSuccess'));
}
public function delete()
{
$user_model = new UserModel();
$user_model->delete($this->user->id);
return redirect()
->route('user_list')
->with('message', lang('User.deleteSuccess'));
}
}
<?php namespace App\Controllers;
<?php
/**
* Class Analytics
* Creates Analytics controller
......@@ -7,6 +7,8 @@
* @link https://castopod.org/
*/
namespace App\Controllers;
use CodeIgniter\Controller;
class Analytics extends Controller
......
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use Myth\Auth\Models\UserModel;
class Auth extends \Myth\Auth\Controllers\AuthController
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* @var array
*/
protected $helpers = ['auth'];
/**
* Displays the login form, or redirects
* the user to their destination/home if
* they are already logged in.
*/
public function changePassword()
{
return view('auth/change_password', [
'config' => $this->config,
'email' => user()->email,
'token' => user()->reset_hash,
]);
}
public function attemptChange()
{
$users = new 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',
'pass_confirm' => 'required|matches[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 (is_null($user)) {
return redirect()
->back()
->with('error', lang('Auth.forgotNoUser'));
}
// Reset token still valid?
if (
!empty($user->reset_expires) &&
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);
return redirect()
->route('login')
->with('message', lang('Auth.resetSuccess'));
}
}
<?php
namespace App\Controllers;
/**
* Class BaseController
*
......@@ -15,6 +13,8 @@ namespace App\Controllers;
* @package CodeIgniter
*/
namespace App\Controllers;
use CodeIgniter\Controller;
class BaseController extends Controller
......
......@@ -39,130 +39,7 @@ class Episode extends BaseController
return $this->$method();
}
public function create()
{
helper(['form']);
if (
!$this->validate([
'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required|regex_match[[a-zA-Z0-9\-]{1,191}]',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
];
echo view('episode/create', $data);
} else {
$new_episode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getVar('title'),
'slug' => $this->request->getVar('slug'),
'enclosure' => $this->request->getFile('enclosure'),
'pub_date' => $this->request->getVar('pub_date'),
'description' => $this->request->getVar('description'),
'image' => $this->request->getFile('image'),
'explicit' => $this->request->getVar('explicit') or false,
'number' => $this->request->getVar('episode_number'),
'season_number' => $this->request->getVar('season_number')
? $this->request->getVar('season_number')
: null,
'type' => $this->request->getVar('type'),
'author_name' => $this->request->getVar('author_name'),
'author_email' => $this->request->getVar('author_email'),
'block' => $this->request->getVar('block') or false,
]);
$episode_model = new EpisodeModel();
$episode_model->save($new_episode);
return redirect()->to(
base_url(
route_to(
'episode_view',
$this->podcast->name,
$new_episode->slug
)
)
);
}
}
public function edit()
{
helper(['form']);
if (
!$this->validate([
'enclosure' =>
'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required|regex_match[[a-zA-Z0-9\-]{1,191}]',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
echo view('episode/edit', $data);
} else {
$this->episode->title = $this->request->getVar('title');
$this->episode->slug = $this->request->getVar('slug');
$this->episode->pub_date = $this->request->getVar('pub_date');
$this->episode->description = $this->request->getVar('description');
$this->episode->explicit =
($this->request->getVar('explicit') or false);
$this->episode->number = $this->request->getVar('episode_number');
$this->episode->season_number = $this->request->getVar(
'season_number'
)
? $this->request->getVar('season_number')
: null;
$this->episode->type = $this->request->getVar('type');
$this->episode->author_name = $this->request->getVar('author_name');
$this->episode->author_email = $this->request->getVar(
'author_email'
);
$this->episode->block = ($this->request->getVar('block') or false);
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $this->request->getFile(
'enclosure'
);
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $this->request->getFile('image');
}
$episode_model = new EpisodeModel();
$episode_model->save($this->episode);
return redirect()->to(
base_url(
route_to(
'episode_view',
$this->podcast->name,
$this->episode->slug
)
)
);
}
}
public function view()
public function index()
{
// The page cache is set to a decade so it is deleted manually upon podcast update
$this->cachePage(DECADE);
......@@ -173,16 +50,6 @@ class Episode extends BaseController
'podcast' => $this->podcast,
'episode' => $this->episode,
];
return view('episode/view', $data);
}
public function delete()
{
$episode_model = new EpisodeModel();
$episode_model->delete($this->episode->id);
return redirect()->to(
base_url(route_to('podcast_view', $this->podcast->name))
);
return view('episode', $data);
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
......
......@@ -19,9 +19,7 @@ class Home extends BaseController
// check if there's only one podcast to redirect user to it
if (count($all_podcasts) == 1) {
return redirect()->to(
base_url(route_to('podcast_view', $all_podcasts[0]->name))
);
return redirect()->route('podcast', [$all_podcasts[0]->name]);
}
// default behavior: list all podcasts on home page
......
......@@ -6,8 +6,6 @@
*/
namespace App\Controllers;
use App\Models\CategoryModel;
use App\Models\LanguageModel;
use App\Models\PodcastModel;
class Podcast extends BaseController
......@@ -29,131 +27,7 @@ class Podcast extends BaseController
return $this->$method();
}
public function create()
{
helper(['form', 'misc']);
$podcast_model = new PodcastModel();
if (
!$this->validate([
'title' => 'required',
'name' => 'required|regex_match[[a-zA-Z0-9\_]{1,191}]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
'browser_lang' => get_browser_language(
$this->request->getServer('HTTP_ACCEPT_LANGUAGE')
),
];
echo view('podcast/create', $data);
} else {
$podcast = new \App\Entities\Podcast([
'title' => $this->request->getVar('title'),
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description'),
'episode_description_footer' => $this->request->getVar(
'episode_description_footer'
),
'image' => $this->request->getFile('image'),
'language' => $this->request->getVar('language'),
'category' => $this->request->getVar('category'),
'explicit' => $this->request->getVar('explicit') or false,
'author_name' => $this->request->getVar('author_name'),
'author_email' => $this->request->getVar('author_email'),
'owner_name' => $this->request->getVar('owner_name'),
'owner_email' => $this->request->getVar('owner_email'),
'type' => $this->request->getVar('type'),
'copyright' => $this->request->getVar('copyright'),
'block' => $this->request->getVar('block') or false,
'complete' => $this->request->getVar('complete') or false,
'custom_html_head' => $this->request->getVar(
'custom_html_head'
),
]);
$podcast_model->save($podcast);
return redirect()->to(
base_url(route_to('podcast_view', $podcast->name))
);
}
}
public function edit()
{
helper(['form', 'misc']);
if (
!$this->validate([
'title' => 'required',
'name' => 'required|regex_match[[a-zA-Z0-9\_]{1,191}]',
'description' => 'required|max_length[4000]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'owner_email' => 'required|valid_email',
'type' => 'required',
])
) {
$languageModel = new LanguageModel();
$categoryModel = new CategoryModel();
$data = [
'podcast' => $this->podcast,
'languages' => $languageModel->findAll(),
'categories' => $categoryModel->findAll(),
];
echo view('podcast/edit', $data);
} else {
$this->podcast->title = $this->request->getVar('title');
$this->podcast->name = $this->request->getVar('name');
$this->podcast->description = $this->request->getVar('description');
$this->podcast->episode_description_footer = $this->request->getVar(
'episode_description_footer'
);
$image = $this->request->getFile('image');
if ($image->isValid()) {
$this->podcast->image = $this->request->getFile('image');
}
$this->podcast->language = $this->request->getVar('language');
$this->podcast->category = $this->request->getVar('category');
$this->podcast->explicit =
($this->request->getVar('explicit') or false);
$this->podcast->author_name = $this->request->getVar('author_name');
$this->podcast->author_email = $this->request->getVar(
'author_email'
);
$this->podcast->owner_name = $this->request->getVar('owner_name');
$this->podcast->owner_email = $this->request->getVar('owner_email');
$this->podcast->type = $this->request->getVar('type');
$this->podcast->copyright = $this->request->getVar('copyright');
$this->podcast->block = ($this->request->getVar('block') or false);
$this->podcast->complete =
($this->request->getVar('complete') or false);
$this->podcast->custom_html_head = $this->request->getVar(
'custom_html_head'
);
$podcast_model = new PodcastModel();
$podcast_model->save($this->podcast);
return redirect()->to(
base_url(route_to('podcast_view', $this->podcast->name))
);
}
}
public function view()
public function index()
{
// The page cache is set to a decade so it is deleted manually upon podcast update
$this->cachePage(DECADE);
......@@ -164,14 +38,6 @@ class Podcast extends BaseController
'podcast' => $this->podcast,
'episodes' => $this->podcast->episodes,
];
return view('podcast/view', $data);
}
public function delete()
{
$podcast_model = new PodcastModel();
$podcast_model->delete($this->podcast->id);
return redirect()->to(base_url(route_to('home')));
return view('podcast', $data);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment