Move tagged posts to another tag ♻️ Refactored managing tags 🔧 Loader vue component

This commit is contained in:
MarceauKa 2019-09-29 16:33:58 +02:00
parent f7b558ed36
commit 4b829dc843
13 changed files with 218 additions and 77 deletions

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Api\Manage;
use App\Http\Controllers\Controller;
use App\Post;
use App\Tag;
use Illuminate\Http\Request;
class TagsController extends Controller
{
public function all()
{
$tags = Tag::withCount('posts')
->orderByDesc('posts_count')
->get();
return response()->json($tags);
}
public function move(Request $request, string $from, string $to)
{
$posts = Post::withAnyTags($from)->get();
$posts->each(function ($item) use ($to) {
$item->attachTag($to);
});
return $this->delete($request, $from);
}
public function delete(Request $request, string $tag)
{
$tag = Tag::findNamedOrCreate($tag);
$tag->delete();
return response()->json();
}
}

View File

@ -22,31 +22,6 @@ class ManageController extends Controller
$this->middleware('auth');
}
public function tags()
{
$tags = Tag::withCount('posts')
->orderByDesc('posts_count')
->get();
return view('manage.tags')->with([
'page_title' => __('Tags'),
'tags' => $tags,
]);
}
public function deleteTag(Request $request, string $tag, string $hash)
{
if ($hash != csrf_token()) {
abort(403);
}
$tag = Tag::findNamedOrCreate($tag);
$tag->delete();
$this->flash(__('Tag :name has been deleted', ['name' => $tag->name]), 'success');
return redirect()->back();
}
public function importForm(Request $request)
{
return view('manage.import')->with([
@ -138,4 +113,11 @@ class ManageController extends Controller
'page_title' => __('Logins'),
]);
}
public function tags()
{
return view('manage.tags')->with([
'page_title' => __('Tags'),
]);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Observers;
use App\Tag;
use Illuminate\Support\Facades\DB;
class TagObserver
{
public function deleted(Tag $tag)
{
DB::table('taggables')
->where('tag_id', $tag->id)
->delete();
}
}

View File

@ -2,35 +2,21 @@
namespace App\Providers;
use App\Events\LinkArchiveRequested;
use App\Listeners\MakeLinkArchive;
use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
LinkArchiveRequested::class => [
MakeLinkArchive::class
\App\Events\LinkArchiveRequested::class => [
\App\Listeners\MakeLinkArchive::class
]
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
//
\App\Tag::observe(\App\Observers\TagObserver::class);
}
}

View File

@ -1,5 +1,14 @@
# Unreleased
## Added
- Move tagged posts to another tag
- Loader vue component
## Changed
- Refactored managing tags
## Fixed
- Missing translations (custom background images)

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=c933c95d37104954a50b",
"/js/app.js": "/js/app.js?id=cd52e6201c26087f8be3",
"/css/app.css": "/css/app.css?id=a737fd0068ee13780419"
}

View File

@ -0,0 +1,18 @@
<template>
<div class="d-flex align-content-center align-items-center" v-if="loading">
<div class="spinner-grow mr-2" role="status"></div>
<div>{{ __("Loading") }}</div>
</div>
</template>
<script>
export default {
props: {
loading: {
type: Boolean,
required: false,
default: false,
}
}
}
</script>

View File

@ -0,0 +1,107 @@
<template>
<div class="card">
<div class="card-header">{{ __('Tags') }}</div>
<div class="card-body" v-if="!loading">
<div class="alert alert-info" v-if="tags.length === 0">
{{ __('No tag') }}
</div>
<table class="table table-borderless table-sm" v-else>
<thead>
<tr>
<th class="w-25">{{ __('Name') }}</th>
<th class="w-25">{{ __('Posts') }}</th>
<th class="w-50">{{ __('Actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="tag in tags">
<td class="align-middle">
<a :href="tag.url">{{ tag.name }}</a>
</td>
<td class="align-middle">{{ tag.posts_count }}</td>
<td class="d-flex justify-content-between">
<select name="tag" id="tag" class="form-control mr-1 w-auto flex-grow-1" @change="move(tag.name, $event.target.value)">
<option value="none">-- {{ __('Move') }} --</option>
<option v-for="item in tags"
:value="item.name"
v-text="item.name"
v-if="tag.name !== item.name"
></option>
</select>
<confirm tag="button" class="btn btn-danger btn-sm"
:text="__('Delete')"
:text-confirm="__('Confirm')"
@confirmed="remove(tag)"
></confirm>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-body" v-else>
<loader :loading="loading"></loader>
</div>
</div>
</template>
<script>
export default {
data() {
return {
tags: [],
loading: true,
}
},
mounted() {
this.fetch();
},
methods: {
fetch() {
this.loading = true;
axios.get('/api/manage/tags')
.then(response => {
this.tags = response.data;
this.loading = false;
})
.catch(error => {
console.log(error);
this.$toasted.error(this.__("Can't fetch tags"));
})
},
move(from, to) {
if (to === 'none') {
return;
}
if (confirm(this.__("All elements tagged :from will be moved to :to. Selected tag will be deleted. Are you sure?", {from, to}))) {
this.loading = true;
axios.post(`/api/manage/tags/${from}/move/${to}`).then(response => {
this.$toasted.success(this.__("Elements tagged :from have been moved to :to.", {from: from, to: to}));
this.fetch();
}).catch(error => {
console.log(error);
})
}
},
remove(tag) {
this.loading = true;
axios.delete(`/api/manage/tags/${tag.name}`).then(response => {
this.$toasted.success(this.__("Tag :name has been deleted", {name: tag.name}));
this.fetch();
}).catch(error => {
console.log(error);
})
},
},
}
</script>

View File

@ -65,6 +65,7 @@
"Logins": "Connexions",
"Import": "Importer",
"Export": "Exporter",
"Actions": "Actions",
"More": "Plus",
"Update preview": "Actualiser la prévisualisation",
"Manage archive": "Gestion de l'archive",
@ -76,12 +77,14 @@
"Save": "Enregistrer",
"Save then archive": "Enregistrer puis archiver",
"Add": "Ajouter",
"Move": "Déplacer",
"Show": "Afficher",
"Hide": "Cacher",
"Copy": "Copier",
"Copied": "Copié",
"Choose": "Choisir",
"Reset": "Réinitialiser",
"Loading": "Chargement",
"Type / to search": "Tapez / pour chercher",
@ -129,6 +132,8 @@
"Search or type a tag": "Cherchez ou tapez un tag",
"Can't fetch tags": "Impossible de récupérer les tags",
"Tag :name has been deleted": "Le tag :name a été supprimé",
"All elements tagged :from will be moved to :to. Selected tag will be deleted. Are you sure?": "Tous les éléments taggés :from seront déplacés vers :to. Le tag sera également supprimé. Êtes-vous sûr ?",
"Elements tagged :from have been moved to :to.": "Les éléments taggés :from ont été déplacés vers :to.",
"Rapid share": "Ajout rapide",
"Configure your rapid share button and drag it to your bookmarks menu.": "Configurez votre bouton d'ajout rapide et faite le glisser dans votre barre de favoris.",

View File

@ -3,38 +3,7 @@
@section('content')
<div class="row justify-content-center">
<div class="col-12">
<div class="card">
<div class="card-header">{{ __('Tags') }}</div>
<div class="card-body">
@if($tags->isEmpty())
<div class="alert alert-info">{{ __('No tag') }}</div>
@else
<table class="table">
<thead>
<tr>
<td>{{ __('Name') }}</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">
<confirm tag="button" class="btn btn-danger btn-sm" text="{{ __('Delete') }}" text-confirm="{{ __('Confirm') }}" href="{{ route('manage.tags.delete', [$tag->name, csrf_token()]) }}"></confirm>
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
</div>
<manage-tags></manage-tags>
</div>
</div>
@endsection

View File

@ -16,3 +16,14 @@ Route::put('story/{id}', 'StoryController@update')->name('story.update');
Route::post('chest', 'ChestController@store')->name('chest.store');
Route::put('chest/{id}', 'ChestController@update')->name('chest.update');
Route::group([
'as' => 'manage.',
'prefix' => 'manage',
'middleware' => 'auth:api',
'namespace' => 'Manage',
], function (\Illuminate\Routing\Router $router) {
$router->get('tags', 'TagsController@all')->name('all');
$router->delete('tags/{tag}', 'TagsController@delete')->name('delete');
$router->post('tags/{from}/move/{to}', 'TagsController@move')->name('move');
});

View File

@ -41,7 +41,6 @@ Route::post('manage/import', 'ManageController@importStore');
Route::get('manage/export', 'ManageController@exportForm')->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/logins', 'ManageController@logins')->name('manage.logins');
Route::get('manage/settings', 'ManageController@settingsForm')->name('manage.settings');
Route::post('manage/settings', 'ManageController@settingsStore');