1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-01-17 21:28:30 +01:00

Add RSS feeds for lists and tags links (#197)

This commit is contained in:
Kovah 2021-03-29 09:34:26 +02:00
parent e8aec55015
commit 80e73ba1d4
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
11 changed files with 180 additions and 143 deletions

View File

@ -11,7 +11,7 @@ use Illuminate\Http\Response;
class FeedController extends Controller
{
public function links(Request $request)
public function links(Request $request): Response
{
$links = Link::latest()->with('user')->get();
$meta = [
@ -27,7 +27,7 @@ class FeedController extends Controller
]), 200, ['Content-Type' => 'application/xml']);
}
public function lists(Request $request)
public function lists(Request $request): Response
{
$lists = LinkList::latest()->with('user')->get();
$meta = [
@ -43,7 +43,23 @@ class FeedController extends Controller
]), 200, ['Content-Type' => 'application/xml']);
}
public function tags(Request $request)
public function listLinks(Request $request, LinkList $list): Response
{
$links = $list->links()->with('user')->latest()->get();
$meta = [
'title' => $list->name,
'link' => $request->fullUrl(),
'updated' => now()->toRfc3339String(),
'id' => $request->fullUrl(),
];
return new Response(view('actions.feed.links', [
'meta' => $meta,
'links' => $links,
]), 200, ['Content-Type' => 'application/xml']);
}
public function tags(Request $request): Response
{
$tags = Tag::latest()->with('user')->get();
$meta = [
@ -58,4 +74,20 @@ class FeedController extends Controller
'tags' => $tags,
]), 200, ['Content-Type' => 'application/xml']);
}
public function tagLinks(Request $request, Tag $tag): Response
{
$links = $tag->links()->with('user')->latest()->get();
$meta = [
'title' => $tag->name,
'link' => $request->fullUrl(),
'updated' => now()->toRfc3339String(),
'id' => $request->fullUrl(),
];
return new Response(view('actions.feed.links', [
'meta' => $meta,
'links' => $links,
]), 200, ['Content-Type' => 'application/xml']);
}
}

View File

@ -43,6 +43,23 @@ class FeedController extends Controller
]), 200, ['Content-Type' => 'application/xml']);
}
public function listLinks(Request $request, $listID)
{
$list = LinkList::publicOnly()->findOrFail($listID);
$links = $list->links()->publicOnly()->latest()->with('user')->get();
$meta = [
'title' => $list->name,
'link' => $request->fullUrl(),
'updated' => now()->toRfc3339String(),
'id' => $request->fullUrl(),
];
return new Response(view('actions.feed.links', [
'meta' => $meta,
'links' => $links,
]), 200, ['Content-Type' => 'application/xml']);
}
public function tags(Request $request)
{
$tags = Tag::publicOnly()->latest()->with('user')->get();
@ -58,4 +75,21 @@ class FeedController extends Controller
'tags' => $tags,
]), 200, ['Content-Type' => 'application/xml']);
}
public function tagLinks(Request $request, $tagID)
{
$tag = Tag::publicOnly()->findOrFail($tagID);
$links = $tag->links()->publicOnly()->latest()->with('user')->get();
$meta = [
'title' => $tag->name,
'link' => $request->fullUrl(),
'updated' => now()->toRfc3339String(),
'id' => $request->fullUrl(),
];
return new Response(view('actions.feed.links', [
'meta' => $meta,
'links' => $links,
]), 200, ['Content-Type' => 'application/xml']);
}
}

View File

@ -45,7 +45,7 @@ class TagController extends Controller
$tag = Tag::publicOnly()->findOrFail($tagID);
$links = $tag->links()
->privateOnly(false)
->privateOnly()
->orderBy(
$request->input('orderBy', 'title'),
$request->input('orderDir', 'ASC')

View File

@ -1,131 +0,0 @@
<div class="card">
<header class="card-header">
@lang('link.add')
</header>
<div class="card-body">
<form action="{{ route('links.store') }}" method="POST">
@csrf
<div class="form-group">
<label class="label" for="url">@lang('link.url')</label>
<input name="url" id="url" type="url"
class="form-control form-control-lg{{ $errors->has('url') ? ' is-invalid' : '' }}"
placeholder="@lang('placeholder.link_url')" value="{{ old('url') ?: $bookmark_url ?? '' }}"
required autofocus>
<p class="invalid-feedback {{ $errors->has('url') ? 'd-none' : '' }}">
@lang('validation.unique', ['attribute' => trans('link.url')])
</p>
@if ($errors->has('url'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('url') }}
</p>
@endif
</div>
<div class="row">
<div class="col-12 col-sm-6 col-md-7">
<div class="form-group">
<label class="label" for="title">@lang('link.title')</label>
<input name="title" id="title"
class="form-control{{ $errors->has('title') ? ' is-invalid' : '' }}"
type="text" placeholder="@lang('placeholder.link_title')"
value="{{ old('title') ?: $bookmark_title ?? '' }}">
@if ($errors->has('title'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('title') }}
</p>
@endif
</div>
<div class="form-group">
<label for="description">@lang('link.description')</label>
<textarea name="description" id="description" rows="4" class="form-control"
>{{ old('description') }}</textarea>
@if ($errors->has('description'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('description') }}
</p>
@endif
</div>
</div>
<div class="col-12 col-sm-6 col-md-5">
<div class="form-group">
<label for="lists">@lang('list.lists')</label>
<input name="lists" id="lists" type="text" placeholder="@lang('placeholder.list_select')"
class="tags-select" value="{{ old('lists') }}" data-tag-type="lists">
@if ($errors->has('lists'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('lists') }}
</p>
@endif
</div>
<div class="form-group">
<label for="tags">@lang('tag.tags')</label>
<input name="tags" id="tags" type="text" placeholder="@lang('placeholder.tags_select')"
class="tags-select" value="{{ old('tags') }}" data-tag-type="tags">
@if ($errors->has('tags'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('tags') }}
</p>
@endif
<div class="tag-suggestions mt-2 d-none">
<small>@lang('We found some tags which might be interesting...')</small>
<div class="tag-suggestions-content mt-1"></div>
</div>
</div>
<div class="form-group">
<label for="is_private">@lang('linkace.is_private')</label>
<select id="is_private" name="is_private"
class="custom-select{{ $errors->has('is_private') ? ' is-invalid' : '' }}">
<option value="0">
@lang('linkace.no')
</option>
<option value="1" @if(usersettings('links_private_default') === '1') selected @endif>
@lang('linkace.yes')
</option>
</select>
@if ($errors->has('is_private'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('is_private') }}
</p>
@endif
</div>
</div>
</div>
<div class="mt-3 d-sm-flex align-items-center justify-content-end">
@if(!isset($bookmark_url))
<div class="custom-control custom-checkbox mb-3 mb-sm-0 mr-sm-4">
<input class="custom-control-input" type="checkbox" id="reload_view" name="reload_view"
@if(session('reload_view')) checked @endif>
<label class="custom-control-label" for="reload_view">
@lang('linkace.continue_adding')
</label>
</div>
@endif
<button type="submit" class="btn btn-primary">
<x-icon.save class="mr-2"/> @lang('link.add')
</button>
</div>
</form>
</div>
</div>

View File

@ -12,7 +12,7 @@
</h3>
<a href="{{ route('guest.lists.feed') }}" class="ml-auto btn btn-sm btn-outline-secondary">
<x-icon.feed/>
<span class="sr-only">@lang('linkace.add')</span>
<span class="sr-only">@lang('linkace.feed')</span>
</a>
<div class="dropdown ml-1">
@include('models.lists.partials.index-order-dropdown', ['baseRoute' => 'lists.index'])

View File

@ -3,8 +3,15 @@
@section('content')
<div class="card">
<header class="card-header">
@lang('list.list')
<header class="card-header d-flex align-items-center">
<span class="mr-3">@lang('list.list')</span>
<a href="{{ route('guest.lists.links.feed', ['list' => $list]) }}"
class="ml-auto btn btn-xs btn-outline-secondary">
<x-icon.feed/>
<span class="sr-only">@lang('linkace.feed')</span>
</a>
</header>
<div class="card-body">

View File

@ -12,7 +12,7 @@
</h3>
<a href="{{ route('guest.tags.feed') }}" class="ml-auto btn btn-sm btn-outline-secondary">
<x-icon.feed/>
<span class="sr-only">@lang('linkace.add')</span>
<span class="sr-only">@lang('linkace.feed')</span>
</a>
</header>

View File

@ -3,13 +3,18 @@
@section('content')
<div class="card">
<header class="card-header">
@lang('tag.tag')
<header class="card-header d-flex align-items-center">
<span class="mr-3">@lang('tag.tag')</span>
<a href="{{ route('guest.tags.links.feed', ['tag' => $tag]) }}"
class="ml-auto btn btn-xs btn-outline-secondary">
<x-icon.feed/>
<span class="sr-only">@lang('linkace.feed')</span>
</a>
</header>
<div class="card-body">
<h2 class="mb-0">{{ $tag->name }}</h2>
</div>
</div>

View File

@ -58,7 +58,9 @@ Route::get('cron/{token}', CronController::class)->name('cron');
Route::group(['middleware' => 'auth:api'], function () {
Route::get('links/feed', [FeedController::class, 'links'])->name('links.feed');
Route::get('lists/feed', [FeedController::class, 'lists'])->name('lists.feed');
Route::get('lists/{list}/feed', [FeedController::class, 'listLinks'])->name('lists.links.feed');
Route::get('tags/feed', [FeedController::class, 'tags'])->name('tags.feed');
Route::get('tags/{tag}/feed', [FeedController::class, 'tagLinks'])->name('tags.links.feed');
});
// Model routes
@ -139,7 +141,9 @@ Route::prefix('guest')->middleware(['guestaccess'])->group(function () {
Route::get('links/feed', [GuestFeedController::class, 'links'])->name('guest.links.feed');
Route::get('lists/feed', [GuestFeedController::class, 'lists'])->name('guest.lists.feed');
Route::get('lists/{list}/feed', [GuestFeedController::class, 'listLinks'])->name('guest.lists.links.feed');
Route::get('tags/feed', [GuestFeedController::class, 'tags'])->name('guest.tags.feed');
Route::get('tags/{tag}/feed', [GuestFeedController::class, 'tagLinks'])->name('guest.tags.links.feed');
Route::resource('links', GuestLinkController::class)
->only(['index'])

View File

@ -50,6 +50,22 @@ class FeedControllerTest extends TestCase
->assertSee($list->name);
}
public function testListLinkFeed(): void
{
$link = LinkList::factory()->create();
$listLink = Link::factory()->create();
$unrelatedLink = Link::factory()->create();
$listLink->lists()->sync([$link->id]);
$response = $this->getAuthorized('lists/1/feed');
$response->assertOk()
->assertSee($link->name)
->assertSee($listLink->url)
->assertDontSee($unrelatedLink->url);
}
public function testTagsFeed(): void
{
$tag = Tag::factory()->create();
@ -60,6 +76,22 @@ class FeedControllerTest extends TestCase
->assertSee($tag->name);
}
public function testTagLinkFeed(): void
{
$tag = Tag::factory()->create();
$tagLink = Link::factory()->create();
$unrelatedLink = Link::factory()->create();
$tagLink->tags()->sync([$tag->id]);
$response = $this->getAuthorized('tags/1/feed');
$response->assertOk()
->assertSee($tag->name)
->assertSee($tagLink->url)
->assertDontSee($unrelatedLink->url);
}
/**
* Send an authorized request for the GET method.
*

View File

@ -47,6 +47,33 @@ class FeedControllerTest extends TestCase
->assertDontSee($listPrivate->name);
}
public function testListLinkFeed(): void
{
$link = LinkList::factory()->create(['is_private' => false]);
$listLink = Link::factory()->create(['is_private' => false]);
$privateListLink = Link::factory()->create(['is_private' => true]);
$unrelatedLink = Link::factory()->create();
$listLink->lists()->sync([$link->id]);
$response = $this->get('guest/lists/1/feed');
$response->assertOk()
->assertSee($link->name)
->assertSee($listLink->url)
->assertDontSee($privateListLink->url)
->assertDontSee($unrelatedLink->url);
}
public function testPrivateListLinkFeed(): void
{
LinkList::factory()->create(['is_private' => true]);
$response = $this->get('guest/lists/1/feed');
$response->assertNotFound();
}
public function testTagsFeed(): void
{
$tagPublic = Tag::factory()->create(['is_private' => false]);
@ -58,4 +85,31 @@ class FeedControllerTest extends TestCase
->assertSee($tagPublic->name)
->assertDontSee($tagPrivate->name);
}
public function testTagLinkFeed(): void
{
$tag = Tag::factory()->create(['is_private' => false]);
$tagLink = Link::factory()->create(['is_private' => false]);
$privateTagLink = Link::factory()->create(['is_private' => true]);
$unrelatedLink = Link::factory()->create();
$tagLink->tags()->sync([$tag->id]);
$response = $this->get('guest/tags/1/feed');
$response->assertOk()
->assertSee($tag->name)
->assertSee($tagLink->url)
->assertDontSee($privateTagLink->url)
->assertDontSee($unrelatedLink->url);
}
public function testPrivateTagLinkFeed(): void
{
Tag::factory()->create(['is_private' => true]);
$response = $this->get('guest/tags/1/feed');
$response->assertNotFound();
}
}