Commit 5c56f3e6 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(settings): add general config for instance (site name, description and icon)

parent 193b373b
......@@ -137,9 +137,11 @@ node_modules
# public folder
public/*
public/media/site
!public/media
!public/.htaccess
!public/favicon.ico
!public/icon*
!public/index.php
!public/robots.txt
......
......@@ -427,4 +427,24 @@ class App extends BaseConfig
* Defines the root folder for media files storage
*/
public string $mediaRoot = 'media';
/**
* --------------------------------------------------------------------------
* Instance / Site Config
* --------------------------------------------------------------------------
*/
public string $siteName = 'Castopod';
public string $siteDescription = 'Castopod Host is an open-source hosting platform made for podcasters who want engage and interact with their audience.';
/**
* @var array<int|string, string>
*/
public array $siteIcon = [
'ico' => '/favicon.ico',
'64' => '/icon-64.png',
'180' => '/icon-180.png',
'192' => '/icon-192.png',
'512' => '/icon-512.png',
];
}
......@@ -48,9 +48,13 @@ $routes->addPlaceholder(
* --------------------------------------------------------------------
*/
$routes->get('manifest.webmanifest', 'WebmanifestController', [
'as' => 'webmanifest',
]);
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'HomeController::index', [
$routes->get('/', 'HomeController', [
'as' => 'home',
]);
......
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\ResponseInterface;
class WebmanifestController extends Controller
{
public function index(): ResponseInterface
{
$webmanifest = [
'name' => service('settings')
->get('App.siteName'),
'description' => service('settings')
->get('App.siteDescription'),
'display' => 'minimal-ui',
'theme_color' => '#009486',
'icons' => [
[
'src' => service('settings')
->get('App.siteIcon')['192'],
'type' => 'image/png',
'sizes' => '192x192',
],
[
'src' => service('settings')
->get('App.siteIcon')['512'],
'type' => 'image/png',
'sizes' => '512x512',
],
],
];
return $this->response->setJSON($webmanifest);
}
}
......@@ -40,6 +40,18 @@ class AuthSeeder extends Seeder
* @var array<string, array<string, string|array>[]>
*/
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',
......
......@@ -29,6 +29,9 @@ if (! function_exists('save_media')) {
if (! file_exists($mediaRoot)) {
mkdir($mediaRoot, 0777, true);
}
if (! file_exists($mediaRoot . '/index.html')) {
touch($mediaRoot . '/index.html');
}
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "af4a438816f7adbbd6950d93ed333e9f",
"content-hash": "f35a050323bdc632cd550f9d13f0679c",
"packages": [
{
"name": "brick/math",
......@@ -60,6 +60,46 @@
],
"time": "2021-01-20T22:51:39+00:00"
},
{
"name": "chrisjean/php-ico",
"version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/chrisbliss18/php-ico.git",
"reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chrisbliss18/php-ico/zipball/ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
"reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.2.4"
},
"type": "library",
"autoload": {
"classmap": ["class-php-ico.php"]
},
"notification-url": "https://packagist.org/downloads/",
"license": ["GPL-2.0+"],
"authors": [
{
"name": "Chris Jean",
"homepage": "https://chrisjean.com",
"role": "Developer"
}
],
"description": "An easy-to-use library to generate valid ICO files.",
"homepage": "https://github.com/chrisbliss18/php-ico",
"keywords": ["favicon", "ico"],
"support": {
"issues": "https://github.com/chrisbliss18/php-ico/issues",
"source": "https://github.com/chrisbliss18/php-ico"
},
"time": "2016-09-27T22:00:56+00:00"
},
{
"name": "codeigniter4/codeigniter4",
"version": "dev-develop",
......@@ -137,6 +177,59 @@
},
"time": "2021-06-10T06:40:05+00:00"
},
{
"name": "codeigniter4/settings",
"version": "dev-develop",
"source": {
"type": "git",
"url": "https://github.com/codeigniter4/settings.git",
"reference": "5d758e5e0a3f9dda9f66303d82ccfbb82e979577"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/codeigniter4/settings/zipball/5d758e5e0a3f9dda9f66303d82ccfbb82e979577",
"reference": "5d758e5e0a3f9dda9f66303d82ccfbb82e979577",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0"
},
"require-dev": {
"codeigniter4/codeigniter4": "dev-develop",
"fakerphp/faker": "^1.9",
"mockery/mockery": "^1.0",
"nexusphp/tachycardia": "^1.0",
"php-coveralls/php-coveralls": "^2.4",
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.3"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Sparks\\Settings\\": "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": "Settings library for CodeIgniter 4",
"homepage": "https://github.com/codeigniter4/settings",
"keywords": ["Settings", "codeigniter", "codeigniter4"],
"support": {
"issues": "https://github.com/codeigniter4/settings/issues",
"source": "https://github.com/codeigniter4/settings/tree/develop"
},
"time": "2021-08-16T05:07:59+00:00"
},
{
"name": "composer/ca-bundle",
"version": "1.2.10",
......@@ -494,16 +587,16 @@
},
{
"name": "james-heinrich/getid3",
"version": "2.0.x-dev",
"version": "v2.0.0-beta4",
"source": {
"type": "git",
"url": "https://github.com/JamesHeinrich/getID3.git",
"reference": "ee238d552571c6029898b087d5fc95df826418d6"
"reference": "5ad79104e937e7d9c8a9141a97e1f063dd1123f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/ee238d552571c6029898b087d5fc95df826418d6",
"reference": "ee238d552571c6029898b087d5fc95df826418d6",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/5ad79104e937e7d9c8a9141a97e1f063dd1123f8",
"reference": "5ad79104e937e7d9c8a9141a97e1f063dd1123f8",
"shasum": ""
},
"require": {
......@@ -562,9 +655,9 @@
"keywords": ["audio", "codecs", "id3", "metadata", "tags", "video"],
"support": {
"issues": "https://github.com/JamesHeinrich/getID3/issues",
"source": "https://github.com/JamesHeinrich/getID3/tree/2.0"
"source": "https://github.com/JamesHeinrich/getID3/tree/v2.0.0-beta4"
},
"time": "2021-12-15T17:29:14+00:00"
"time": "2021-10-06T16:23:45+00:00"
},
{
"name": "kint-php/kint",
......@@ -745,16 +838,16 @@
},
{
"name": "league/commonmark",
"version": "1.6.2",
"version": "1.6.6",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "7d70d2f19c84bcc16275ea47edabee24747352eb"
"reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/7d70d2f19c84bcc16275ea47edabee24747352eb",
"reference": "7d70d2f19c84bcc16275ea47edabee24747352eb",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c4228d11e30d7493c6836d20872f9582d8ba6dcf",
"reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf",
"shasum": ""
},
"require": {
......@@ -772,7 +865,7 @@
"github/gfm": "0.29.0",
"michelf/php-markdown": "~1.4",
"mikehaertl/php-shellcommand": "^1.4",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan": "^0.12.90",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
"scrutinizer/ocular": "^1.5",
"symfony/finder": "^4.2"
......@@ -838,37 +931,40 @@
"type": "tidelift"
}
],
"time": "2021-05-12T11:39:41+00:00"
"time": "2021-07-17T17:13:23+00:00"
},
{
"name": "league/html-to-markdown",
"version": "4.10.0",
"version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "0868ae7a552e809e5cd8f93ba022071640408e88"
"reference": "e5600a2c5ce7b7571b16732c7086940f56f7abec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0868ae7a552e809e5cd8f93ba022071640408e88",
"reference": "0868ae7a552e809e5cd8f93ba022071640408e88",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e5600a2c5ce7b7571b16732c7086940f56f7abec",
"reference": "e5600a2c5ce7b7571b16732c7086940f56f7abec",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xml": "*",
"php": ">=5.3.3"
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"mikehaertl/php-shellcommand": "~1.1.0",
"phpunit/phpunit": "^4.8|^5.7",
"scrutinizer/ocular": "~1.1"
"mikehaertl/php-shellcommand": "^1.1.0",
"phpstan/phpstan": "^0.12.82",
"phpunit/phpunit": "^8.5 || ^9.2",
"scrutinizer/ocular": "^1.6",
"unleashedtech/php-coding-standard": "^2.7",
"vimeo/psalm": "^4.6"
},
"bin": ["bin/html-to-markdown"],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.10-dev"
"dev-master": "5.1-dev"
}
},
"autoload": {
......@@ -897,7 +993,7 @@
"keywords": ["html", "markdown"],
"support": {
"issues": "https://github.com/thephpleague/html-to-markdown/issues",
"source": "https://github.com/thephpleague/html-to-markdown/tree/4.10.0"
"source": "https://github.com/thephpleague/html-to-markdown/tree/5.0.1"
},
"funding": [
{
......@@ -913,11 +1009,11 @@
"type": "github"
},
{
"url": "https://www.patreon.com/colinodell",
"type": "patreon"
"url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown",
"type": "tidelift"
}
],
"time": "2020-07-01T00:34:03+00:00"
"time": "2021-09-17T20:00:27+00:00"
},
{
"name": "maxmind-db/reader",
......@@ -7457,7 +7553,8 @@
"james-heinrich/getid3": 20,
"myth/auth": 20,
"codeigniter4/codeigniter4": 20,
"michalsn/codeigniter4-uuid": 20
"michalsn/codeigniter4-uuid": 20,
"codeigniter4/settings": 20
},
"prefer-stable": true,
"prefer-lowest": false,
......@@ -7465,5 +7562,5 @@
"php": "^8.0"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.1.0"
}
......@@ -17,7 +17,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/themes',
__DIR__ . '/tests',
__DIR__ . '/public',
__DIR__ . '/public',
]);
$parameters->set(Option::SKIP, [
......
......@@ -18,6 +18,21 @@ $routes->group(
'as' => 'admin',
]);
$routes->group('settings', function ($routes): void {
$routes->get('/', 'SettingsController', [
'as' => 'settings-general',
'filter' => 'permission:settings-manage',
]);
$routes->post('instance', 'SettingsController::attemptInstanceEdit', [
'as' => 'settings-instance',
'filter' => 'permission:settings-manage',
]);
$routes->get('instance-delete-icon', 'SettingsController::deleteIcon', [
'as' => 'settings-instance-delete-icon',
'filter' => 'permission:settings-manage',
]);
});
$routes->group('persons', function ($routes): void {
$routes->get('/', 'PersonController', [
'as' => 'person-list',
......
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace Modules\Admin\Controllers;
use CodeIgniter\HTTP\RedirectResponse;
use PHP_ICO;
class SettingsController extends BaseController
{
public function index(): string
{
helper('form');
return view('settings/general');
}
public function attemptInstanceEdit(): RedirectResponse
{
$rules = [
'site_icon' =>
'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_squared[site_icon]|min_dims[image,512,512]|permit_empty',
];
if (! $this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$siteName = $this->request->getPost('site_name');
if ($siteName !== service('settings')->get('App.siteName')) {
service('settings')->set('App.siteName', $siteName);
}
$siteDescription = $this->request->getPost('site_description');
if ($siteDescription !== service('settings')->get('App.siteDescription')) {
service('settings')->set('App.siteDescription', $siteDescription);
}
$siteIconFile = $this->request->getFile('site_icon');
if ($siteIconFile !== null && $siteIconFile->isValid()) {
helper(['filesystem', 'media']);
// delete site folder in media before repopulating it
delete_files(ROOTPATH . 'public/media/site/');
// save original in disk
$originalFilename = save_media($siteIconFile, 'site', 'icon');
// convert jpeg image to png if not
if ($siteIconFile->getClientMimeType() !== 'image/png') {
service('image')->withFile(ROOTPATH . 'public/media/' . $originalFilename)
->convert(IMAGETYPE_JPEG)
->save(ROOTPATH . 'public/media/site/icon.png');
}
// generate random hash to use as a suffix to renew browser cache
$randomHash = substr(bin2hex(random_bytes(18)), 0, 8);
// generate ico
$ico_lib = new PHP_ICO();
$ico_lib->add_image(ROOTPATH . 'public/media/site/icon.png', [[32, 32], [64, 64]]);
$ico_lib->save_ico(ROOTPATH . "public/media/site/favicon.{$randomHash}.ico");
// resize original to needed sizes
foreach ([64, 180, 192, 512] as $size) {
service('image')
->withFile(ROOTPATH . 'public/media/site/icon.png')
->resize($size, $size)
->save(ROOTPATH . "public/media/site/icon-{$size}.{$randomHash}.png");
}
service('settings')
->set('App.siteIcon', [
'ico' => "/media/site/favicon.{$randomHash}.ico",
'64' => "/media/site/icon-64.{$randomHash}.png",
'180' => "/media/site/icon-180.{$randomHash}.png",
'192' => "/media/site/icon-192.{$randomHash}.png",
'512' => "/media/site/icon-512.{$randomHash}.png",
]);
}
return redirect()->back();
}
public function deleteIcon(): RedirectResponse
{
helper('filesystem');
// delete site folder in media
delete_files(ROOTPATH . 'public/media/site/');
service('settings')
->forget('App.siteIcon');
return redirect()->back();
}
}
......@@ -29,6 +29,8 @@ return [
'pages' => 'Pages',
'page-list' => 'All pages',
'page-create' => 'New Page',
'settings' => 'Settings',
'settings-general' => 'General',
'account' => [
'my-account' => 'My account',
'change-password' => 'Change password',
......
......@@ -16,6 +16,7 @@ return [
'episodes' => 'episodes',
'contributors' => 'contributors',
'pages' => 'pages',
'settings' => 'settings',
'add' => 'add',
'new' => 'new',
'edit' => 'edit',
......
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
return [
'title' => 'General settings',
'form' => [
'site_section_title' => 'Instance',
'site_icon' => 'Site icon',
'site_icon_delete' => 'Delete site icon',
'site_icon_hint' => 'Site icons are what you see on your browser tabs, bookmarks bar, and when you add a website as a shortcut on mobile devices.',
'site_icon_helper' => 'Icon must be squared with at least 512px wide and tall.',
'site_name' => 'Site name',
'site_description' => 'Site description',
'submit' => 'Save',
],
];
......@@ -28,6 +28,8 @@ return [
'pages' => 'Pages',
'page-list' => 'Toutes les pages',
'page-create' => 'Créer une page',
'settings' => 'Paramètres',
'settings-general' => 'Général',
'account' => [
'my-account' => 'Mon compte',
'change-password' => 'Modifier le mot de passe',
......
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
return [
'title' => 'Paramètres généraux',
'form' => [
'site_section_title' => 'Instance',
'site_section_subtitle' => 'configuration de l’instance',
'site_icon' => 'Favicon du site',
'site_icon_delete' => 'Supprimer la favicon du site',