mirror of https://github.com/MarceauKa/shaark.git
✨ Manage tags
This commit is contained in:
parent
83a1e1c55d
commit
2a71485c5d
|
@ -4,21 +4,49 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Http\Requests\ImportRequest;
|
||||
use App\Services\Import;
|
||||
use App\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ImportController extends Controller
|
||||
class ManageController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function form(Request $request)
|
||||
public function tags()
|
||||
{
|
||||
return view('import')->with(['page_title' => 'Importer']);
|
||||
$tags = Tag::withCount('posts')
|
||||
->orderByDesc('posts_count')
|
||||
->get();
|
||||
|
||||
return view('manage.tags')->with([
|
||||
'page_title' => __('Tags'),
|
||||
'tags' => $tags,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(ImportRequest $request)
|
||||
public function deleteTag(Request $request, string $tag, string $hash)
|
||||
{
|
||||
if ($hash != csrf_token()) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$tag = Tag::findNamedOrCreate($tag);
|
||||
$tag->delete();
|
||||
|
||||
$this->flash("Le tag \"{$tag->name}\" a été supprimé !", 'success');
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function importForm(Request $request)
|
||||
{
|
||||
return view('manage.import')->with([
|
||||
'page_title' => __('Import')
|
||||
]);
|
||||
}
|
||||
|
||||
public function importStore(ImportRequest $request)
|
||||
{
|
||||
try {
|
||||
$import = new Import(
|
|
@ -46,6 +46,7 @@
|
|||
"Whoops!": "Oups !",
|
||||
"Whoops, something went wrong on our servers.": "Oups, quelque chose s'est mal passé sur nos serveurs.",
|
||||
"Manage": "Gestion",
|
||||
"Tags": "Tags",
|
||||
"Link": "Lien",
|
||||
"Story": "Story",
|
||||
"Chest": "Coffre",
|
||||
|
|
|
@ -23,6 +23,10 @@ nav.navbar,
|
|||
box-shadow: $box-shadow-sm;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: $box-shadow-sm;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Import') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
Attention, l'import a été testé avec la version <code>0.0.41</code> de Shaarli. Faites une sauvegarde avant toute tentative.
|
||||
</div>
|
||||
|
||||
<p class="card-text">
|
||||
Vous pouvez importer votre contenu depuis le <strong>Shaarli originel</strong>. Importez le fichier <code>data/datastore.php</code> (par défaut)
|
||||
et vérifiez qu'il commence par <code><?php /* </code> et se termine par <code>*/ ?></code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<form method="POST" action="{{ route('import') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label for="file">Fichier d'import</label>
|
||||
<input type="file" class="form-control-file {{ $errors->has('file') ? ' is-invalid' : '' }}" name="file" id="file" accept=".php">
|
||||
@error('file')
|
||||
<span class="invalid-feedback" role="alert">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="ignore_tags" id="ignore_tags">
|
||||
<label class="custom-control-label" for="ignore_tags">Ignorer les tags ?</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="get_extras" id="get_extras">
|
||||
<label class="custom-control-label" for="get_extras">Chercher les extras ?</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="refresh_search" id="refresh_search">
|
||||
<label class="custom-control-label" for="refresh_search">Actualiser recherche ?</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-primary">{{ __('Importer') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -1,89 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@if(isset($page_title)){{ $page_title }} - @endif{{ config('app.name') }}</title>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@if(auth()->check())
|
||||
<meta name="api-token" content="{{ auth()->user()->api_token }}">
|
||||
@endif
|
||||
@stack('meta')
|
||||
@include('feed::links')
|
||||
<link rel="dns-prefetch" href="//fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
|
||||
@stack('css')
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<nav class="navbar navbar-expand-md navbar-light mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ url('/') }}">
|
||||
{{ config('app.name', 'Laravel Shaarli') }}
|
||||
</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>
|
||||
</button>
|
||||
|
||||
@can('restricted')
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<search url="{{ route('api.search') }}" id="search"></search>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
@guest
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="nav-item dropdown">
|
||||
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
|
||||
{{ Auth::user()->name }} <span class="caret"></span>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
|
||||
<h6 class="dropdown-header">{{ __('Contents') }}</h6>
|
||||
<a class="dropdown-item" href="{{ route('link.create') }}">{{ __('Add link') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('story.create') }}">{{ __('Add story') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('chest.create') }}">{{ __('Add chest') }}</a>
|
||||
<h6 class="dropdown-header">{{ __('Manage') }}</h6>
|
||||
<a class="dropdown-item" href="{{ route('account') }}">{{ __('Compte') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('import') }}">{{ __('Import') }}</a>
|
||||
<h6 class="dropdown-header">{{ __('Session') }}</h6>
|
||||
<a class="dropdown-item" href="{{ route('logout') }}"
|
||||
onclick="event.preventDefault();
|
||||
document.getElementById('logout-form').submit();">
|
||||
{{ __('Logout') }}
|
||||
</a>
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
|
||||
@csrf
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
@endguest
|
||||
</ul>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@include('layouts.partials.navbar')
|
||||
<main class="py-4">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p class="text-center">
|
||||
{{ config('app.name') }} -
|
||||
<a href="{{ route('feeds.main') }}">{{ __('RSS Feed') }}</a> -
|
||||
<a href="https://github.com/MarceauKa/laravel-shaarli">{{ __('Source code') }}</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@include('partials.flash')
|
||||
@include('layouts.partials.footer')
|
||||
</div>
|
||||
|
||||
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||
@stack('js')
|
||||
@include('layouts.partials.scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}">
|
||||
<head>
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<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="list-group">
|
||||
<a href="{{ route('manage.import') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/import') ? ' active' : '' }}"
|
||||
>{{ __('Import') }}</a>
|
||||
<a href="{{ route('manage.tags') }}"
|
||||
class="list-group-item list-group-item-action{{ request()->is('manage/tags') ? ' active' : '' }}"
|
||||
>{{ __('Tags') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-8">
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@include('layouts.partials.footer')
|
||||
</div>
|
||||
@include('layouts.partials.scripts')
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
<footer>
|
||||
<p class="text-center">
|
||||
{{ config('app.name') }} -
|
||||
<a href="{{ route('feeds.main') }}">{{ __('RSS Feed') }}</a> -
|
||||
<a href="https://github.com/MarceauKa/laravel-shaarli">{{ __('Source code') }}</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@include('partials.flash')
|
|
@ -0,0 +1,13 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@if(isset($page_title)){{ $page_title }} - @endif{{ config('app.name') }}</title>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@if(auth()->check())
|
||||
<meta name="api-token" content="{{ auth()->user()->api_token }}">
|
||||
@endif
|
||||
@stack('meta')
|
||||
@include('feed::links')
|
||||
<link rel="dns-prefetch" href="//fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
|
||||
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
|
||||
@stack('css')
|
|
@ -0,0 +1,55 @@
|
|||
<nav class="navbar navbar-expand-md navbar-light mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ url('/') }}">
|
||||
{{ config('app.name', 'Laravel Shaarli') }}
|
||||
</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>
|
||||
</button>
|
||||
|
||||
@can('restricted')
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<search url="{{ route('api.search') }}" id="search"></search>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
@guest
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="nav-item dropdown">
|
||||
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
|
||||
{{ Auth::user()->name }} <span class="caret"></span>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
|
||||
<h6 class="dropdown-header">{{ __('Contents') }}</h6>
|
||||
|
||||
<a class="dropdown-item" href="{{ route('link.create') }}">{{ __('Add link') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('story.create') }}">{{ __('Add story') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('chest.create') }}">{{ __('Add chest') }}</a>
|
||||
|
||||
<h6 class="dropdown-header">{{ __('Manage') }}</h6>
|
||||
|
||||
<a class="dropdown-item" href="{{ route('account') }}">{{ __('Compte') }}</a>
|
||||
<a class="dropdown-item" href="{{ route('manage.tags') }}">{{ __('Données') }}</a>
|
||||
|
||||
<h6 class="dropdown-header">{{ __('Session') }}</h6>
|
||||
|
||||
<a class="dropdown-item" href="{{ route('logout') }}"
|
||||
onclick="event.preventDefault();
|
||||
document.getElementById('logout-form').submit();">
|
||||
{{ __('Logout') }}
|
||||
</a>
|
||||
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
|
||||
@csrf
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
@endguest
|
||||
</ul>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,2 @@
|
|||
<script src="{{ mix('js/app.js') }}" defer></script>
|
||||
@stack('js')
|
|
@ -0,0 +1,66 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Import') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
Attention, l'import a été testé avec la version <code>0.0.41</code> de Shaarli. Faites une sauvegarde avant toute tentative.
|
||||
</div>
|
||||
|
||||
<p class="card-text">
|
||||
Vous pouvez importer votre contenu depuis le <strong>Shaarli originel</strong>. Importez le fichier <code>data/datastore.php</code> (par défaut)
|
||||
et vérifiez qu'il commence par <code><?php /* </code> et se termine par <code>*/ ?></code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<form method="POST" action="{{ route('manage.import') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label for="file">Fichier d'import</label>
|
||||
<input type="file" class="form-control-file {{ $errors->has('file') ? ' is-invalid' : '' }}" name="file" id="file" accept=".php">
|
||||
@error('file')
|
||||
<span class="invalid-feedback" role="alert">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="ignore_tags" id="ignore_tags">
|
||||
<label class="custom-control-label" for="ignore_tags">Ignorer les tags ?</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="get_extras" id="get_extras">
|
||||
<label class="custom-control-label" for="get_extras">Chercher les extras ?</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" name="refresh_search" id="refresh_search">
|
||||
<label class="custom-control-label" for="refresh_search">Actualiser recherche ?</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-primary">{{ __('Importer') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -0,0 +1,40 @@
|
|||
@extends('layouts.manage')
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Import') }}</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if($tags->isEmpty())
|
||||
<div class="alert alert-info">Aucun tag.</div>
|
||||
@else
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Nom</td>
|
||||
<td>Posts</td>
|
||||
<td class="text-right">#</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($tags as $tag)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ $tag->url }}">{{ $tag->name }}</a>
|
||||
</td>
|
||||
<td>{{ $tag->posts_count }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{{ route('manage.tags.delete', [$tag->name, csrf_token()]) }}" class="btn btn-danger btn-sm">Supprimer</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
|
@ -33,5 +33,8 @@ Route::get('chest/{chest}', 'BrowseController@chest')->name('chest.view');
|
|||
Route::get('account', 'AccountController@form')->name('account');
|
||||
Route::post('account', 'AccountController@store');
|
||||
Route::post('account/password', 'AccountController@storePassword')->name('account.password');
|
||||
Route::get('import', 'ImportController@form')->name('import');
|
||||
Route::post('import', 'ImportController@store');
|
||||
|
||||
Route::get('manage/import', 'ManageController@importForm')->name('manage.import');
|
||||
Route::post('manage/import', 'ManageController@importStore');
|
||||
Route::get('manage/tags', 'ManageController@tags')->name('manage.tags');
|
||||
Route::get('manage/tags/delete/{tag}/{hash}', 'ManageController@deleteTag')->name('manage.tags.delete');
|
||||
|
|
Loading…
Reference in New Issue