mirror of https://github.com/MarceauKa/shaark.git
✨ Settings ✨ Dark mode
This commit is contained in:
parent
0ae23728c0
commit
ad4d061f4a
|
@ -6,7 +6,9 @@ use App\Exports\ChestsExport;
|
|||
use App\Exports\LinksExport;
|
||||
use App\Exports\StoriesExport;
|
||||
use App\Http\Requests\ImportRequest;
|
||||
use App\Http\Requests\StoreSettingsRequest;
|
||||
use App\Services\Import;
|
||||
use App\Services\Shaarli\Shaarli;
|
||||
use App\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
@ -105,4 +107,21 @@ class ManageController extends Controller
|
|||
$format == 'csv' ? \Maatwebsite\Excel\Excel::CSV : \Maatwebsite\Excel\Excel::XLSX
|
||||
);
|
||||
}
|
||||
|
||||
public function settingsForm(Request $request)
|
||||
{
|
||||
return view('manage.settings')->with([
|
||||
'page_title' => __('Settings'),
|
||||
'settings' => app('shaarli')->getSettings(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function settingsStore(StoreSettingsRequest $request, Shaarli $shaarli)
|
||||
{
|
||||
$validated = collect($request->validated());
|
||||
$shaarli->setSettings($validated);
|
||||
|
||||
$this->flash(__('Settings updated!'), 'success');
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,46 +6,12 @@ use Closure;
|
|||
|
||||
class CheckForGlobalPrivacy
|
||||
{
|
||||
/** @var array $except */
|
||||
private $except = [
|
||||
'login',
|
||||
'password/*',
|
||||
];
|
||||
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$user = null;
|
||||
|
||||
foreach (['web', 'api'] as $guard) {
|
||||
if (! $user) {
|
||||
$user = auth($guard)->user();
|
||||
}
|
||||
}
|
||||
|
||||
if ($user || false === $this->globalPrivacyEnabled() || $this->inExceptArray($request)) {
|
||||
if (app('shaarli')->authorizeFromRequest($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
protected function globalPrivacyEnabled(): bool
|
||||
{
|
||||
return config('app.private', false);
|
||||
}
|
||||
|
||||
protected function inExceptArray($request): bool
|
||||
{
|
||||
foreach ($this->except as $except) {
|
||||
if ($except !== '/') {
|
||||
$except = trim($except, '/');
|
||||
}
|
||||
|
||||
if ($request->fullUrlIs($except) || $request->is($except)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreSettingsRequest extends FormRequest
|
||||
{
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
'min:2',
|
||||
'max:100',
|
||||
],
|
||||
'is_private' => [
|
||||
'nullable',
|
||||
'in:on,off',
|
||||
],
|
||||
'is_dark' => [
|
||||
'nullable',
|
||||
'in:on,off',
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\Shaarli\Shaarli;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
|
@ -12,6 +13,12 @@ class AppServiceProvider extends ServiceProvider
|
|||
if ($this->app->environment('production')) {
|
||||
$this->app['request']->server->set('HTTPS', true);
|
||||
}
|
||||
|
||||
$this->app->singleton(Shaarli::class, function ($app) {
|
||||
return new Shaarli($app);
|
||||
});
|
||||
|
||||
$this->app->alias(Shaarli::class, 'shaarli');
|
||||
}
|
||||
|
||||
public function boot()
|
||||
|
|
|
@ -27,7 +27,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||
$this->registerPolicies();
|
||||
|
||||
Gate::define('restricted', function (?User $user) {
|
||||
if (config('app.private') === false) {
|
||||
if (app('shaarli')->getIsPrivate() === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Shaarli;
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Valuestore\Valuestore;
|
||||
|
||||
/**
|
||||
* @method string getName()
|
||||
* @method bool getIsPrivate()
|
||||
*/
|
||||
class Shaarli
|
||||
{
|
||||
/** @var string VERSION */
|
||||
public const VERSION = '1.0.0';
|
||||
/** @var Application $app */
|
||||
protected $app;
|
||||
/** @var Valuestore $settings */
|
||||
protected $settings;
|
||||
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->settings = Valuestore::make(storage_path('settings.json'));
|
||||
|
||||
foreach ($this->app['config']->get('shaarli') as $key => $item) {
|
||||
if ($this->settings->has($key) === false) {
|
||||
$this->settings->put($key, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function authorizeFromRequest(Request $request): bool
|
||||
{
|
||||
$user = null;
|
||||
|
||||
if ($this->getIsPrivate() === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (['web', 'api'] as $guard) {
|
||||
if (! $user) {
|
||||
$user = auth($guard)->user();
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($user) || $this->requestAuthorizedForGlobalPrivacy($request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function requestAuthorizedForGlobalPrivacy(Request $request): bool
|
||||
{
|
||||
$excepts = [
|
||||
'login',
|
||||
'password/*',
|
||||
];
|
||||
|
||||
foreach ($excepts as $except) {
|
||||
if ($except !== '/') {
|
||||
$except = trim($except, '/');
|
||||
}
|
||||
|
||||
if ($request->fullUrlIs($except) || $request->is($except)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSettings(): array
|
||||
{
|
||||
return $this->settings->all();
|
||||
}
|
||||
|
||||
public function setSettings(Collection $settings): void
|
||||
{
|
||||
$this->settings->put('name', $settings->get('name'));
|
||||
$this->settings->put('is_private', $settings->get('is_private') == 'on');
|
||||
$this->settings->put('is_dark', $settings->get('is_dark') == 'on');
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (substr($name, 0, 3) === 'get') {
|
||||
$key = Str::snake(substr($name, 3));
|
||||
|
||||
if ($this->settings->has($key)) {
|
||||
return $this->settings->get($key);
|
||||
}
|
||||
|
||||
if (array_key_exists($key, config('shaarli'))) {
|
||||
$this->settings->put($key, config('shaarli')[$key]);
|
||||
return $this->settings->get($key);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException("Method {$name} does not exists.");
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
"laravel/tinker": "^1.0",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"spatie/laravel-feed": "^2.3",
|
||||
"spatie/valuestore": "^1.2",
|
||||
"teamtnt/laravel-scout-tntsearch-driver": "^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "47e960a2a94937000a0a01f6dcc47e10",
|
||||
"content-hash": "69dc31c906fede8ca4998f7d3ce078f8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "dnoegel/php-xdg-base-dir",
|
||||
|
@ -2211,6 +2211,59 @@
|
|||
],
|
||||
"time": "2019-08-22T07:12:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/valuestore",
|
||||
"version": "1.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/valuestore.git",
|
||||
"reference": "798897f7d571aa0a62786ae531d573d3c6af55d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/valuestore/zipball/798897f7d571aa0a62786ae531d573d3c6af55d0",
|
||||
"reference": "798897f7d571aa0a62786ae531d573d3c6af55d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\Valuestore\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Freek Van der Herten",
|
||||
"role": "Developer",
|
||||
"email": "freek@spatie.be",
|
||||
"homepage": "https://spatie.be"
|
||||
},
|
||||
{
|
||||
"name": "Jolita Grazyte",
|
||||
"role": "Developer",
|
||||
"email": "jolita@spatie.be",
|
||||
"homepage": "https://spatie.be"
|
||||
}
|
||||
],
|
||||
"description": "Easily store some values",
|
||||
"homepage": "https://github.com/spatie/valuestore",
|
||||
"keywords": [
|
||||
"json",
|
||||
"spatie",
|
||||
"valuestore"
|
||||
],
|
||||
"time": "2019-02-15T10:56:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
"version": "v6.2.1",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
return [
|
||||
'name' => env('APP_NAME', 'Laravel Shaarli'),
|
||||
'private' => env('APP_PRIVATE', false),
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
'debug' => env('APP_DEBUG', false),
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'name' => env('APP_NAME'),
|
||||
'is_private' => false,
|
||||
'is_dark' => false,
|
||||
];
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
It's a **free and open source platform** to host by yourself.
|
||||
|
||||
**Shaarli** allows you to **save your web links** (websites, youtube videos, ...), to **share your stories** and **manage your web accounts**.
|
||||
All of your content can be **private or public** and can be browsed by **tags** or **all-in-one search**.
|
||||
**Shaarli** allows you to **save your web links** (websites, youtube videos, ...), to **share your stories** and
|
||||
**manage your web accounts**. All of your content can be **private or public** and can be browsed by **tags** or **all-in-one search**.
|
||||
|
||||
It's ready to use for **production**. **Laravel Shaarli** is inspired by [Shaarli](https://github.com/shaarli/Shaarli) but built with [Laravel](https://github.com/laravel/laravel).
|
||||
It's ready to use for **production**. **Laravel Shaarli** is inspired by [Shaarli](https://github.com/shaarli/Shaarli)
|
||||
but built with [Laravel](https://github.com/laravel/laravel) and [Vue.js](https://vuejs.org/).
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -24,7 +25,7 @@ It's ready to use for **production**. **Laravel Shaarli** is inspired by [Shaarl
|
|||
- [x] Original Shaarli import
|
||||
- [x] RSS feed
|
||||
- [x] Export
|
||||
- [x] i18n
|
||||
- [x] i18n (english and french)
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
"Contents": "Contenus",
|
||||
"Session": "Session",
|
||||
"Account": "Compte",
|
||||
"Data": "Données",
|
||||
"Import": "Importer",
|
||||
"Export": "Exporter",
|
||||
"More": "Plus",
|
||||
|
@ -115,6 +114,11 @@
|
|||
"Width": "Largeur",
|
||||
"Height": "Hauteur",
|
||||
"Share button": "Bouton d'ajout rapide",
|
||||
"Settings": "Paramètres",
|
||||
"Site name": "Nom du site",
|
||||
"Private content (all content is private and login is required)": "Contenu privé (tout est privé et nécessite d'être connecté)",
|
||||
"Dark mode": "Mode sombre",
|
||||
"Settings updated!": "Paramètres mis à jour !",
|
||||
"Update account": "Modifier le compte",
|
||||
"Your account has been updated!": "Votre compte a été mis à jour !",
|
||||
"Update password": "Modifier le mot de passe",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
body.dark {
|
||||
background-image: linear-gradient(to top, #505285 0%, #585e92 12%, #65689f 25%, #7474b0 37%, #7e7ebb 50%, #8389c7 62%, #9795d4 75%, #a2a1dc 87%, #b5aee4 100%);
|
||||
filter: hue-rotate(180deg) invert(1);
|
||||
|
||||
img,
|
||||
video,
|
||||
iframe {
|
||||
filter: hue-rotate(180deg) invert(1);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
@import '~bootstrap/scss/bootstrap';
|
||||
@import '~vue-multiselect/dist/vue-multiselect.min.css';
|
||||
@import '~mavon-editor/dist/css/index.css';
|
||||
@import "dark";
|
||||
|
||||
html,
|
||||
body {
|
||||
|
@ -11,7 +12,7 @@ body {
|
|||
}
|
||||
|
||||
body {
|
||||
background-image: linear-gradient(to top, #e6e9f0 0%, #eef1f5 100%);
|
||||
background-image: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
|
@ -116,3 +117,4 @@ nav.navbar,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<body class="{{ app('shaarli')->getIsDark() ? 'dark' : 'light' }}">
|
||||
<div id="app">
|
||||
@include('layouts.partials.navbar')
|
||||
<main class="py-4">
|
||||
|
|
|
@ -3,14 +3,17 @@
|
|||
<head>
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<body class="{{ app('shaarli')->getIsDark() ? 'dark' : 'light' }}">
|
||||
<div id="app">
|
||||
@include('layouts.partials.navbar')
|
||||
<main class="py-4">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="col-12 col-md-4 mb-3">
|
||||
<div class="list-group">
|
||||
<a href="{{ route('manage.settings') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/settings') ? ' active' : '' }}"
|
||||
>{{ __('Settings') }}</a>
|
||||
<a href="{{ route('manage.tags') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/tags') ? ' active' : '' }}"
|
||||
>{{ __('Tags') }}</a>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<footer>
|
||||
<p class="text-center">
|
||||
{{ config('app.name') }} -
|
||||
{{ config('app.name') }} - v{{ app('shaarli')::VERSION }} -
|
||||
<a href="{{ route('feeds.main') }}">{{ __('RSS Feed') }}</a> -
|
||||
<a href="https://github.com/MarceauKa/laravel-shaarli">{{ __('Source code') }}</a>
|
||||
</p>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<nav class="navbar navbar-expand-md navbar-light mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ url('/') }}">
|
||||
{{ config('app.name', 'Laravel Shaarli') }}
|
||||
{{ app('shaarli')->getName() }}
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<h6 class="dropdown-header">{{ __('Manage') }}</h6>
|
||||
|
||||
<a class="dropdown-item" href="{{ route('account') }}">{{ __('Account') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('manage.tags') }}">{{ __('Data') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('manage.settings') }}">{{ __('Settings') }}</a>
|
||||
|
||||
<h6 class="dropdown-header">{{ __('Session') }}</h6>
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Settings') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('manage.settings') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">{{ __('Site name') }}</label>
|
||||
<input type="text" class="form-control {{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" id="name" value="{{ $settings['name'] }}">
|
||||
@error('name')
|
||||
<span class="invalid-feedback" role="alert">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="is_private" id="is_private" {{ $settings['is_private'] ? ' checked' : '' }}>
|
||||
<label class="custom-control-label" for="is_private">{{ __('Private content (all content is private and login is required)') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="is_dark" id="is_dark" {{ $settings['is_dark'] ? ' checked' : '' }}>
|
||||
<label class="custom-control-label" for="is_dark">{{ __('Dark mode') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">{{ __('Save') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -37,6 +37,8 @@ Route::post('account/password', 'AccountController@storePassword')->name('accoun
|
|||
Route::get('manage/import', 'ManageController@importForm')->name('manage.import');
|
||||
Route::post('manage/import', 'ManageController@importStore');
|
||||
Route::get('manage/export', 'ManageController@exportForm')->name('manage.export');
|
||||
Route::post('manage/export', 'ManageController@export')->name('manage.export');
|
||||
Route::post('manage/export', 'ManageController@export');
|
||||
Route::get('manage/tags', 'ManageController@tags')->name('manage.tags');
|
||||
Route::get('manage/tags/delete/{tag}/{hash}', 'ManageController@deleteTag')->name('manage.tags.delete');
|
||||
Route::get('manage/settings', 'ManageController@settingsForm')->name('manage.settings');
|
||||
Route::post('manage/settings', 'ManageController@settingsStore');
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"is_private":false,"name":"Shaarli","is_dark":false}
|
Loading…
Reference in New Issue