1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-01-16 12:48:25 +01:00

Add several new features (#862 #863 #864)

- contact page with editable content
- additional footer link
- custom navbar title / logo text
- small optimizations for guest mode
This commit is contained in:
Kovah 2024-11-16 11:55:02 +01:00
parent bba868deba
commit 0dccd1a7f8
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
19 changed files with 304 additions and 22 deletions

View File

@ -41,8 +41,11 @@ class SystemSettingsController extends Controller
public function updateGuest(SystemSettingsUpdateRequest $request): RedirectResponse
{
$guestSettings = app(GuestSettings::class);
$systemSettings = app(SystemSettings::class);
$settings = $request->except(['_token', 'guest_share']);
$systemSettings->guest_access_enabled = $request->input('guest_access_enabled');
$settings = $request->except(['_token', 'guest_share', 'guest_access_enabled']);
foreach ($settings as $key => $value) {
$guestSettings->$key = $value;
@ -56,6 +59,7 @@ class SystemSettingsController extends Controller
}
}
$systemSettings->save();
$guestSettings->save();
flash(trans('settings.settings_saved'));

View File

@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers;
use App\Settings\SystemSettings;
class ContactController extends Controller
{
public function __invoke(SystemSettings $systemSettings)
{
if (!$systemSettings->contact_page_enabled) {
abort(404);
}
return view('app.contact', [
'title' => $systemSettings->contact_page_title,
'content' => $systemSettings->contact_page_content,
]);
}
}

View File

@ -28,6 +28,8 @@ class SettingsMiddleware
if ($userLocale = usersettings('locale')) {
app()->setLocale($userLocale);
} else {
app()->setLocale(guestsettings('locale'));
}
return $next($request);

View File

@ -11,14 +11,49 @@ class SystemSettingsUpdateRequest extends FormRequest
return [
'system_page_title' => [
'max:256',
'nullable',
'string',
],
'system_guest_access' => [
'system_logo_text' => [
'max:20',
'nullable',
'string',
],
'additional_footer_link_url' => [
'nullable',
'string',
'required_with:additional_footer_link_text'
],
'additional_footer_link_text' => [
'max:20',
'nullable',
'string',
'required_with:additional_footer_link_url'
],
'contact_page_enabled' => [
'boolean',
],
'contact_page_title' => [
'max:20',
'nullable',
'string',
],
'contact_page_content' => [
'max:10000',
'nullable',
'string',
],
'system_custom_header_content' => [
'nullable',
'string',
],
// Guest settings
'system_guest_access' => [
'boolean',
],
'guest_locale' => [
'string',
],
'guest_listitem_count' => [
'integer',
],

View File

@ -10,6 +10,8 @@ class GuestSettings extends Settings
public bool $links_new_tab;
public int $darkmode_setting;
public string $locale;
public bool $share_email;
public bool $share_buffer;
public bool $share_evernote;

View File

@ -7,9 +7,19 @@ use Spatie\LaravelSettings\Settings;
class SystemSettings extends Settings
{
public ?string $page_title;
public ?string $logo_text;
public ?string $cron_token;
public ?string $custom_header_content;
public ?string $additional_footer_link_url;
public ?string $additional_footer_link_text;
public bool $contact_page_enabled;
public ?string $contact_page_title;
public ?string $contact_page_content;
public bool $guest_access_enabled;
public bool $setup_completed;

View File

@ -7,6 +7,7 @@ return [
* put them (manually) here.
*/
'settings' => [
\App\Settings\GuestSettings::class,
\App\Settings\SystemSettings::class,
\App\Settings\UserSettings::class,
],

View File

@ -0,0 +1,20 @@
<?php
use Spatie\LaravelSettings\Migrations\SettingsMigration;
Class ExtendSystemSettings extends SettingsMigration
{
public function up(): void
{
$this->migrator->add('system.logo_text', null);
$this->migrator->add('system.additional_footer_link_url', null);
$this->migrator->add('system.additional_footer_link_text', null);
$this->migrator->add('system.contact_page_enabled', false);
$this->migrator->add('system.contact_page_title', null);
$this->migrator->add('system.contact_page_content', null);
$this->migrator->add('guest.locale', config('app.fallback_locale'));
}
}

View File

@ -47,6 +47,7 @@ return [
'menu' => 'Menu',
'entries' => 'Entries',
'feed' => 'Feed',
'contact' => 'Contact',
'continue_adding' => 'Continue Adding',

View File

@ -74,11 +74,22 @@ return [
'two_factor_regenerate_recovery_codes' => 'Generate new Recovery Codes',
'page_title' => 'Page Title',
'logo_text' => 'Custom Logo Text',
'guest_access' => 'Enable Guest Access',
'guest_access_help' => 'If enabled, guest will be able to see all links that are not private.',
'custom_header_content' => 'Custom Header Content',
'custom_header_content_help' => 'Content entered here will be placed before the &lt;/head&gt; tag on all LinkAce sites. Useful to place analytics or customization scripts. Caution: contents are not escaped and may break the site!',
'additional_footer_link' => 'Additional Link in the Footer',
'additional_footer_link_url' => 'Link URL',
'additional_footer_link_text' => 'Link Text',
'contact_page' => 'Contact/About Page',
'contact_page_info' => 'The contact/about page can be used to display additional information about your bookmarks. The link is visible in the footer. Markdown is supported.',
'contact_page_enabled' => 'Enable the contact/about page',
'contact_page_title' => 'Custom title for the page',
'contact_page_content' => 'Content of the page',
'cron_token' => 'Cron Token',
'cron_token_generate' => 'Generate Token',
'cron_token_generate_confirm' => 'Do you really want to generate a new token?',

View File

@ -50,6 +50,10 @@ body:not(.bookmarklet) {
}
}
.navbar-brand {
font-family: $font-family-sans-condensed;
}
.card-table {
margin: -#{$card-border-width};

View File

@ -27,17 +27,14 @@
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="guest_access_enabled">
@lang('settings.guest_access')
<label class="form-label" for="logo_text">
@lang('settings.logo_text')
</label>
<select id="guest_access_enabled" name="guest_access_enabled"
class="simple-select {{ $errors->has('guest_access_enabled') ? ' is-invalid' : '' }}">
<x-forms.yes-no-options :setting="systemsettings('guest_access_enabled')"/>
</select>
<p class="small text-pale mt-1">@lang('settings.guest_access_help')</p>
@if ($errors->has('guest_access_enabled'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('guest_access_enabled') }}
<input type="text" id="logo_text" name="logo_text" class="form-control" maxlength="20"
value="{{ old('logo_text') ?: systemsettings('logo_text') }}">
@if ($errors->has('logo_text'))
<p class="invalid-feedback mt-1" role="alert">
{{ $errors->first('logo_text') }}
</p>
@endif
</div>
@ -45,24 +42,122 @@
</div>
</div>
<div class="row my-4">
<div class="col-12"><h5>@lang('settings.additional_footer_link')</h5></div>
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="additional_footer_link_url">
@lang('settings.additional_footer_link_url')
</label>
<input type="url" id="additional_footer_link_url" name="additional_footer_link_url"
class="form-control"
value="{{ old('additional_footer_link_url') ?: systemsettings('additional_footer_link_url') }}">
@if ($errors->has('additional_footer_link_url'))
<p class="invalid-feedback mt-1" role="alert">
{{ $errors->first('additional_footer_link_url') }}
</p>
@endif
</div>
</div>
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="additional_footer_link_text">
@lang('settings.additional_footer_link_text')
</label>
<input type="text" id="additional_footer_link_text" name="additional_footer_link_text"
class="form-control" maxlength="20"
value="{{ old('additional_footer_link_text') ?: systemsettings('additional_footer_link_text') }}">
@if ($errors->has('additional_footer_link_text'))
<p class="invalid-feedback mt-1" role="alert">
{{ $errors->first('additional_footer_link_text') }}
</p>
@endif
</div>
</div>
</div>
<div class="row my-4">
<div class="col-12">
<h5>@lang('settings.contact_page')</h5>
<p class="my-3 small">@lang('settings.contact_page_info')</p>
</div>
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="contact_page_enabled">
@lang('settings.contact_page_enabled')
</label>
<select id="contact_page_enabled" name="contact_page_enabled"
class="simple-select {{ $errors->has('contact_page_enabled') ? ' is-invalid' : '' }}">
<x-forms.yes-no-options :setting="systemsettings('contact_page_enabled')"/>
</select>
<p class="small text-pale mt-1">@lang('settings.guest_access_help')</p>
@if ($errors->has('contact_page_enabled'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('contact_page_enabled') }}
</p>
@endif
</div>
</div>
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="contact_page_title">
@lang('settings.contact_page_title')
</label>
<input type="text" id="contact_page_title" name="contact_page_title"
class="form-control" maxlength="20"
value="{{ old('contact_page_title') ?: systemsettings('contact_page_title') }}">
@if ($errors->has('contact_page_title'))
<p class="invalid-feedback mt-1" role="alert">
{{ $errors->first('contact_page_title') }}
</p>
@endif
</div>
</div>
<div class="col-12">
<div class="mb-4">
<label class="form-label" for="contact_page_content">
@lang('settings.contact_page_content')
</label>
<textarea name="contact_page_content" id="contact_page_content" rows="4"
class="form-control{{ $errors->has('contact_page_content') ? ' is-invalid' : '' }}"
>{{ old('contact_page_content', systemsettings('contact_page_content')) }}</textarea>
@error('contact_page_content')
<p class="invalid-feedback" role="alert">
{{ $errors->first('contact_page_content') }}
</p>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="custom_header_content">
@lang('settings.custom_header_content')
</label>
<textarea name="custom_header_content" id="custom_header_content" rows="4"
class="form-control{{ $errors->has('custom_header_content') ? ' is-invalid' : '' }}"
>{{ old('custom_header_content', systemsettings('custom_header_content')) }}</textarea>
<p class="small text-pale mt-1">@lang('settings.custom_header_content_help')</p>
@error('custom_header_content')
<p class="invalid-feedback" role="alert">
{{ $errors->first('custom_header_content') }}
</p>
@enderror
</div>
</div>
</div>

View File

@ -10,6 +10,52 @@
@csrf
<div class="row mt-4">
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="guest_access_enabled">
@lang('settings.guest_access')
</label>
<select id="guest_access_enabled" name="guest_access_enabled"
class="simple-select {{ $errors->has('guest_access_enabled') ? ' is-invalid' : '' }}">
<x-forms.yes-no-options :setting="systemsettings('guest_access_enabled')"/>
</select>
<p class="small text-pale mt-1">@lang('settings.guest_access_help')</p>
@if ($errors->has('guest_access_enabled'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('guest_access_enabled') }}
</p>
@endif
</div>
</div>
<div class="col-12 col-sm-8 col-md-6">
</div>
</div>
<div class="row mt-4">
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">
<label class="form-label" for="locale">
@lang('settings.locale')
</label>
<select id="locale" name="locale"
class="simple-select {{ $errors->has('locale') ? ' is-invalid' : '' }}">
@foreach(config('app.available_locales') as $key => $locale)
<option value="{{ $key }}"
@if(guestsettings('locale') === $key) selected @endif>
{{ $locale }}
</option>
@endforeach
</select>
@if($errors->has('locale'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('locale') }}
</p>
@endif
</div>
</div>
<div class="col-12 col-sm-8 col-md-6">
<div class="mb-4">

View File

@ -0,0 +1,13 @@
@extends('layouts.app')
@section('content')
<div class="contact-page">
<h3 class="mb-4">{{ $title ?? trans('linkace.contact') }}</h3>
<div class="card mt-4">
<div class="card-body contact-content">
{!! \Illuminate\Support\Str::markdown($content) !!}
</div>
</div>
</div>
@endsection

View File

@ -1,6 +1,16 @@
<aside class="footer container text-center small pt-3 pb-5">
<div>
@lang('linkace.project_of') <a href="https://kovah.de/?utm_source=linkace" rel="noopener" target="_blank">Kovah.de</a>
@if(systemsettings('additional_footer_link_url') && systemsettings('additional_footer_link_text'))
| <a href="{{ systemsettings('additional_footer_link_url') }}" rel="noreferrer noopener" target="_blank">
{{ systemsettings('additional_footer_link_text') }}
</a>
@endif
@if(systemsettings('contact_page_enabled'))
| <a href="{{ route('contact') }}">
{{ systemsettings('contact_page_title') ?? trans('linkace.contact') }}
</a>
@endif
</div>
@auth
<div class="mt-1">

View File

@ -1,8 +1,6 @@
@if(usersettings('darkmode_setting') === 1
|| (request()->is('guest/*') && guestsettings('darkmode_setting') === 1))
@if(usersettings('darkmode_setting') === 1 || (auth()->guest() && guestsettings('darkmode_setting') === 1))
<link href="{{ mix('assets/dist/css/app-dark.css') }}" rel="stylesheet">
@elseif(usersettings('darkmode_setting') === 2
|| (request()->is('guest/*') && guestsettings('darkmode_setting') === 2))
@elseif(usersettings('darkmode_setting') === 2 || (auth()->guest() && guestsettings('darkmode_setting') === 2))
<link rel="stylesheet" media="(prefers-color-scheme: light)" href="{{ mix('assets/dist/css/app.css') }}">
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="{{ mix('assets/dist/css/app-dark.css') }}">
@else

View File

@ -1,7 +1,11 @@
<nav class="navbar navbar-dark navbar-expand bg-primary shadow-sm d-none d-md-flex">
<div class="container">
<a class="navbar-brand" href="{{ route('dashboard') }}">
<x-icon.linkace/>
@if(systemsettings('logo_text'))
{{ systemsettings('logo_text') }}
@else
<x-icon.linkace/>
@endif
</a>
@auth
<ul class="navbar-nav">
@ -62,7 +66,11 @@
<div class="navbar navbar-dark navbar-expand brand-only bg-primary shadow-sm d-md-none">
<div class="container">
<a class="navbar-brand" href="{{ route('dashboard') }}">
<x-icon.linkace/>
@if(systemsettings('logo_text'))
{{ systemsettings('logo_text') }}
@else
<x-icon.linkace/>
@endif
</a>
<ul class="navbar-nav ms-auto">
@include('partials.nav-user')

View File

@ -13,6 +13,7 @@ use App\Http\Controllers\App\ImportController;
use App\Http\Controllers\App\SearchController;
use App\Http\Controllers\App\TrashController;
use App\Http\Controllers\App\UserSettingsController;
use App\Http\Controllers\ContactController;
use App\Http\Controllers\CronController;
use App\Http\Controllers\FetchController;
use App\Http\Controllers\FrontController;
@ -64,6 +65,8 @@ Route::prefix('bookmarklet')->group(function () {
Route::get('login', [BookmarkletController::class, 'getLoginForm'])->name('bookmarklet-login');
});
Route::get('contact', ContactController::class)->name('contact');
Route::get('cron/{token}', CronController::class)->name('cron');
Route::get('auth/accept-invite', [RegistrationController::class, 'acceptInvitation'])

View File

@ -1,9 +1,8 @@
<?php
namespace Controller\API;
namespace Tests\Controller\API;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Controller\API\ApiTestCase;
class GeneralApiTest extends ApiTestCase
{