Merge branch 'master' into 'production'
Master See merge request Romulus21/portal!63
This commit is contained in:
89
app/Http/Controllers/BookmarkController.php
Normal file
89
app/Http/Controllers/BookmarkController.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\BookmarkRequest;
|
||||||
|
use App\Http\Resources\BookmarkCollection;
|
||||||
|
use App\Models\Bookmark;
|
||||||
|
use App\Http\Resources\Bookmark as BookmarkResource;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class BookmarkController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->authorize('viewAny', Bookmark::class);
|
||||||
|
|
||||||
|
return new BookmarkCollection(request()->user()->bookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(BookmarkRequest $request)
|
||||||
|
{
|
||||||
|
$this->authorize('create', Bookmark::class);
|
||||||
|
|
||||||
|
$metas = $this->getMeta($request->url);
|
||||||
|
$request['name'] = (empty($request->name)) ? $metas['title'] : $request->name;
|
||||||
|
$request['favicon'] = $metas['favicon'];
|
||||||
|
|
||||||
|
$bookmark = request()->user()->bookmarks()->create($request->all());
|
||||||
|
|
||||||
|
return (new BookmarkResource($bookmark))
|
||||||
|
->response()
|
||||||
|
->setStatusCode(201);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
$this->authorize('view', $bookmark);
|
||||||
|
|
||||||
|
return new BookmarkResource($bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(BookmarkRequest $request, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
$this->authorize('update', $bookmark);
|
||||||
|
|
||||||
|
$bookmark->update($request->all());
|
||||||
|
|
||||||
|
return (new BookmarkResource($bookmark))
|
||||||
|
->response()
|
||||||
|
->setStatusCode(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
$this->authorize('delete', $bookmark);
|
||||||
|
|
||||||
|
$bookmark->delete();
|
||||||
|
|
||||||
|
return response([], 204);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMeta($url)
|
||||||
|
{
|
||||||
|
$metas = [];
|
||||||
|
$client = new \GuzzleHttp\Client();
|
||||||
|
$promise = $client->requestAsync('GET', $url);
|
||||||
|
$response = $promise->wait();
|
||||||
|
$page = $response->getBody()->getContents();
|
||||||
|
preg_match("/\<title\>(.*)\<\/title\>/i",$page,$title);
|
||||||
|
$metas['title'] = $title[1];
|
||||||
|
|
||||||
|
|
||||||
|
preg_match('/\<link rel="[icon|appel-touch](.*)\"\>/i',$page,$favicon);
|
||||||
|
if(isset($favicon[1])) {
|
||||||
|
preg_match('/href="(.*)/i',$favicon[1],$favicon);
|
||||||
|
$metas['favicon'] = $favicon[1];
|
||||||
|
|
||||||
|
preg_match('/http/', $metas['favicon'], $matches);
|
||||||
|
if(empty($matches)) {
|
||||||
|
$metas['favicon'] = parse_url($url, PHP_URL_SCHEME).'://'.parse_url($url, PHP_URL_HOST).$metas['favicon'];
|
||||||
|
}
|
||||||
|
//dd($metas['favicon'], $matches, !isset($matches[1]), empty($matches));
|
||||||
|
} else {
|
||||||
|
$metas['favicon'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $metas;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Http/Requests/BookmarkRequest.php
Normal file
31
app/Http/Requests/BookmarkRequest.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class BookmarkRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'max:65',
|
||||||
|
'url' => ['required', 'url', 'max:255'],
|
||||||
|
'favicon' => ['url', 'max:255'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attributes()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Nom du marque-page',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'url.required' => 'Une url est nécessaire',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Http/Resources/Bookmark.php
Normal file
34
app/Http/Resources/Bookmark.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class Bookmark extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => [
|
||||||
|
'type' => 'bookmark',
|
||||||
|
'bookmark_id' => $this->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => $this->name,
|
||||||
|
'description' => $this->description,
|
||||||
|
'url' => $this->url,
|
||||||
|
'favicon' => $this->favicon,
|
||||||
|
'created_at' => $this->created_at->diffForHumans(),
|
||||||
|
'last_updated' => $this->updated_at->diffForHumans(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Http/Resources/BookmarkCollection.php
Normal file
25
app/Http/Resources/BookmarkCollection.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||||
|
|
||||||
|
class BookmarkCollection extends ResourceCollection
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource collection into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => $this->collection,
|
||||||
|
'bookmarks_count' => $this->count(),
|
||||||
|
'links' => [
|
||||||
|
'self' => url('/bookmarks'),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/Models/Bookmark.php
Normal file
10
app/Models/Bookmark.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Bookmark extends Model
|
||||||
|
{
|
||||||
|
protected $guarded = [];
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Models\Bookmark;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use App\Models\Image;
|
use App\Models\Image;
|
||||||
use App\Models\Memo;
|
use App\Models\Memo;
|
||||||
@@ -65,6 +66,11 @@ class User extends Authenticatable
|
|||||||
return $this->hasMany(ToDoList::class);
|
return $this->hasMany(ToDoList::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function bookmarks() : HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Bookmark::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function images(): MorphMany
|
public function images(): MorphMany
|
||||||
{
|
{
|
||||||
return $this->morphMany(Image::class, 'imageable');
|
return $this->morphMany(Image::class, 'imageable');
|
||||||
|
|||||||
94
app/Policies/BookmarkPolicy.php
Normal file
94
app/Policies/BookmarkPolicy.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Bookmark;
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
|
||||||
|
class BookmarkPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view any bookmarks.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function viewAny(User $user)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view the bookmark.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Bookmark $bookmark
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function view(User $user, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
return $user->id == $bookmark->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can create bookmarks.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function create(User $user)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can update the bookmark.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Bookmark $bookmark
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function update(User $user, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
return $user->id == $bookmark->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can delete the bookmark.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Bookmark $bookmark
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function delete(User $user, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
return $user->id == $bookmark->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can restore the bookmark.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Bookmark $bookmark
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function restore(User $user, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can permanently delete the bookmark.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Bookmark $bookmark
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function forceDelete(User $user, Bookmark $bookmark)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
'App\User' => 'App\Policies\UserPolicy',
|
'App\User' => 'App\Policies\UserPolicy',
|
||||||
'App\Models\Memo' => 'App\Policies\MemoPolicy',
|
'App\Models\Memo' => 'App\Policies\MemoPolicy',
|
||||||
'App\Models\ToDoList' => 'App\Policies\ToDoListPolicy',
|
'App\Models\ToDoList' => 'App\Policies\ToDoListPolicy',
|
||||||
|
'App\Models\Bookmark' => 'App\Policies\BookmarkPolicy',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
15
database/factories/BookmarkFactory.php
Normal file
15
database/factories/BookmarkFactory.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
||||||
|
|
||||||
|
use App\Models\Bookmark;
|
||||||
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
|
$factory->define(Bookmark::class, function (Faker $faker) {
|
||||||
|
return [
|
||||||
|
'user_id' => factory(\App\User::class),
|
||||||
|
'name' => $faker->words(3, [false]),
|
||||||
|
'url' => $faker->url,
|
||||||
|
'favicon' => $faker->imageUrl(),
|
||||||
|
];
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateBookmarksTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('bookmarks', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('user_id');
|
||||||
|
$table->string('name')->nullable();
|
||||||
|
$table->string('description')->nullable();
|
||||||
|
$table->string('url');
|
||||||
|
$table->string('favicon')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('bookmarks');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,10 @@
|
|||||||
<svg-vue icon="list" />
|
<svg-vue icon="list" />
|
||||||
<span>ToDo Lists</span>
|
<span>ToDo Lists</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<router-link to="/bookmarks" class="nav-item p-2">
|
||||||
|
<svg-vue icon="globe" />
|
||||||
|
<span>Bookmarks</span>
|
||||||
|
</router-link>
|
||||||
<router-link to="/jeux" class="nav-item p-2">
|
<router-link to="/jeux" class="nav-item p-2">
|
||||||
<svg-vue icon="games" />
|
<svg-vue icon="games" />
|
||||||
<span>Jeux</span>
|
<span>Jeux</span>
|
||||||
|
|||||||
6
resources/js/router.js
vendored
6
resources/js/router.js
vendored
@@ -11,6 +11,7 @@ import MemoShow from './views/Memo/MemoShow'
|
|||||||
import MemoEdit from './views/Memo/MemoEdit'
|
import MemoEdit from './views/Memo/MemoEdit'
|
||||||
import ToDoListIndex from './views/ToDoLists/ToDoListIndex'
|
import ToDoListIndex from './views/ToDoLists/ToDoListIndex'
|
||||||
import ToDoListShow from './views/ToDoLists/ToDoListShow'
|
import ToDoListShow from './views/ToDoLists/ToDoListShow'
|
||||||
|
import BookmarkIndex from './views/Bookmark/BookmarkIndex'
|
||||||
import GameIndex from './views/Games/GameIndex'
|
import GameIndex from './views/Games/GameIndex'
|
||||||
import Hangman from './views/Games/HangMan/Hangman'
|
import Hangman from './views/Games/HangMan/Hangman'
|
||||||
import Quizz from './views/Games/Quizz/Quizz'
|
import Quizz from './views/Games/Quizz/Quizz'
|
||||||
@@ -65,6 +66,11 @@ export default new VueRouter({
|
|||||||
meta: {title: 'Details of List'}
|
meta: {title: 'Details of List'}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/bookmarks', component: BookmarkIndex,
|
||||||
|
meta: {title: 'Bookmark Lists'}
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/jeux', component: GameIndex,
|
path: '/jeux', component: GameIndex,
|
||||||
meta: {title: 'Liste des jeux'}
|
meta: {title: 'Liste des jeux'}
|
||||||
|
|||||||
95
resources/js/views/Bookmark/Bookmark.vue
Normal file
95
resources/js/views/Bookmark/Bookmark.vue
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bg-white rounded text-black m-2 p-2">
|
||||||
|
<div v-if="!edit" class="edit flex flex-wrap items-center">
|
||||||
|
<a :href="bookmark.data.attributes.data.url"
|
||||||
|
class="flex-1">
|
||||||
|
<img v-if="bookmark.data.attributes.data.favicon"
|
||||||
|
class="w-5 h-5 mr-2 inline-block"
|
||||||
|
:src="bookmark.data.attributes.data.favicon"
|
||||||
|
alt="">
|
||||||
|
<span class="font-bold">
|
||||||
|
{{ bookmark.data.attributes.data.name }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<span class="edit-icon inline-block text-red cursor-pointer" @click="edit = !edit">
|
||||||
|
<svg-vue icon="edit" class="inline w-4 fill-current cursor-pointer mx-2" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex items-center">
|
||||||
|
<img v-if="bookmark.data.attributes.data.favicon"
|
||||||
|
class="w-5 h-5 mr-2 inline-block"
|
||||||
|
:src="bookmark.data.attributes.data.favicon"
|
||||||
|
alt="">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="name"
|
||||||
|
@keypress.enter="update"
|
||||||
|
class="flex-1 w-10 font-semibold pl-1">
|
||||||
|
<div @click="edit = !edit">
|
||||||
|
<svg-vue icon="edit" class="w-4 fill-current cursor-pointer mx-2" />
|
||||||
|
</div>
|
||||||
|
<div @click="destroy">
|
||||||
|
<svg-vue icon="close" class="w-4 fill-current text-red-dark cursor-pointer mx-2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Bookmark',
|
||||||
|
props: [
|
||||||
|
'bookmark'
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
name: this.bookmark.data.attributes.data.name,
|
||||||
|
url: '',
|
||||||
|
edit: false,
|
||||||
|
errors: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
update() {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
axios.patch('/api/bookmarks/' + this.bookmark.data.bookmark_id, { name: this.name, url: this.bookmark.data.attributes.data.url })
|
||||||
|
.then(() => {
|
||||||
|
this.edit = false
|
||||||
|
})
|
||||||
|
.catch(errorRes => {
|
||||||
|
console.log('Internal Error, Unable to delete contact.' + errorRes)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
axios.delete('/api/bookmarks/' + this.bookmark.data.bookmark_id)
|
||||||
|
.then(() => {
|
||||||
|
this.edit = false
|
||||||
|
this.$emit('update:destroy', this.bookmark.data.bookmark_id)
|
||||||
|
})
|
||||||
|
.catch(errorRes => {
|
||||||
|
console.log('Internal Error, Unable to delete contact.' + errorRes)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
name(val) {
|
||||||
|
return this.bookmark.data.attributes.data.name = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.edit-icon {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit:hover .edit-icon {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
92
resources/js/views/Bookmark/BookmarkIndex.vue
Normal file
92
resources/js/views/Bookmark/BookmarkIndex.vue
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<div v-if="modal" class="modal-container" @click="modal = ! modal"></div>
|
||||||
|
<div v-if="modal" class="modal px-2">
|
||||||
|
<p class="m-2 text-center">Add a new to-do list ?</p>
|
||||||
|
<InputField name="name" label="Title" placeholder="Your Title" @update:field="name = $event" :errors="errors" classes="border" />
|
||||||
|
<InputField name="name" label="Url" placeholder="Website Url" required @update:field="url = $event" :errors="errors" classes="border" />
|
||||||
|
<div class="flex justify-center mx-2 my-4">
|
||||||
|
<button class="btn-primary mr-2" @click="create">Create</button>
|
||||||
|
<button class="btn" @click="modal = ! modal">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between mb-1">
|
||||||
|
<router-link to="/" class="btn">Back</router-link>
|
||||||
|
|
||||||
|
<a href="#" class="btn-primary" @click="modal = ! modal">Add Bookmark</a>
|
||||||
|
</div>
|
||||||
|
<Loader v-if="loading" />
|
||||||
|
<div v-else class="flex flex-wrap -mx-2 mt-2">
|
||||||
|
<div v-if="bookmarks.length < 1" class="font-bold p-2">No Bookmark Yet</div>
|
||||||
|
<div v-else
|
||||||
|
v-for="(bookmark, index) in bookmarks"
|
||||||
|
:key="index"
|
||||||
|
class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4" >
|
||||||
|
<Bookmark :bookmark="bookmark" @update:destroy="deleteBookmark" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Loader from '../../components/Loader'
|
||||||
|
import InputField from '../../components/InputField'
|
||||||
|
import Bookmark from './Bookmark'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BookmarkIndex',
|
||||||
|
components: {
|
||||||
|
Loader, InputField, Bookmark
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
modal: false,
|
||||||
|
bookmarks: null,
|
||||||
|
name: '',
|
||||||
|
url: '',
|
||||||
|
errors: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
axios.get('/api/bookmarks')
|
||||||
|
.then(res => {
|
||||||
|
this.bookmarks = res.data.data
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
.catch(errorRes => {
|
||||||
|
this.loading = false
|
||||||
|
if (errorRes.response.status === 404) {
|
||||||
|
this.$router.push('/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
create: function () {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
axios.post('/api/bookmarks', {name: this.name, url: this.url})
|
||||||
|
.then(res => {
|
||||||
|
console.log(res)
|
||||||
|
this.modal = false
|
||||||
|
this.name = ''
|
||||||
|
this.url = ''
|
||||||
|
this.bookmarks.push(res.data)
|
||||||
|
})
|
||||||
|
.catch(errorRes => {
|
||||||
|
console.log('Internal Error, Unable to delete contact.' + errorRes)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteBookmark: function(e) {
|
||||||
|
this.bookmarks.forEach((bookmark, index) => {
|
||||||
|
if(bookmark.data.bookmark_id === e) {
|
||||||
|
console.log(bookmark, bookmark.data.bookmark_id, index)
|
||||||
|
this.bookmarks.splice(index, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
127
resources/svg/globe.svg
Normal file
127
resources/svg/globe.svg
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M461.82,259.656c-1.148,42.74-10.248,81.592-25.584,116.556h25.18c20.632-34.964,33.044-73.816,34.584-116.556H461.82z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M141.148,395.636H88.912c24.476,42.74,59.532,75.892,100.352,92.168C169.528,466.528,152.972,434.492,141.148,395.636z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M306.736,12.08c19.732,21.28,36.292,53.316,48.108,92.168h52.24C382.608,61.508,347.544,28.356,306.736,12.08z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M416.452,123.672h-56.584c8.8,34.972,13.984,73.82,14.636,116.56h69.256C442.54,197.492,432.744,158.64,416.452,123.672z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M88.912,104.248h52.232c11.824-38.852,28.38-70.888,48.116-92.168C148.444,28.356,113.388,61.508,88.912,104.248z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M79.54,123.672c-16.292,34.972-26.084,73.82-27.304,116.56h69.26c0.652-42.74,5.832-81.592,14.636-116.56H79.54z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M34.18,259.656H0c1.544,42.74,13.952,81.592,34.596,116.556h25.168C44.42,341.244,35.316,302.396,34.18,259.656z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M34.592,123.672C13.952,158.64,1.544,197.492,0,240.232h34.176c1.14-42.74,10.244-81.592,25.584-116.56H34.592z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M121.496,259.656h-69.26c1.216,42.74,11.012,81.592,27.3,116.556h56.596C127.328,341.244,122.148,302.396,121.496,259.656
|
||||||
|
z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M354.844,395.636c-11.816,38.856-28.372,70.892-48.108,92.168c40.808-16.276,75.872-49.428,100.348-92.168H354.844z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M374.504,259.656c-0.652,42.74-5.836,81.592-14.636,116.556h56.584c16.292-34.968,26.088-73.816,27.308-116.556H374.504z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M461.416,123.672h-25.188c15.34,34.972,24.444,73.82,25.588,116.56H496C494.464,197.492,482.048,158.64,461.416,123.672z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M377.9,37.488c19.436,18.536,36.252,39.564,49.632,66.76h22C430.316,77.052,405.964,54.792,377.9,37.488z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M46.464,104.244h22c13.376-27.192,30.18-48.212,49.62-66.76C90.024,54.792,65.676,77.052,46.464,104.244z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M68.456,395.636H46.468c19.208,23.312,43.548,49.452,71.62,66.752C98.644,443.856,81.84,418.948,68.456,395.636z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M427.532,395.64c-13.38,23.312-30.196,48.224-49.632,66.768c28.072-17.32,52.416-43.46,71.64-66.768H427.532z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M255.132,0.78v103.468h80.392C317.288,45.968,290.1,7.124,255.132,0.78z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M160.464,104.244h79.128V0.78C208.508,7.124,178.704,45.968,160.464,104.244z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M239.592,123.672h-84.424c-9.28,34.968-14.912,73.82-15.62,116.56h100.044L239.592,123.672L239.592,123.672z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M340.828,123.672h-85.696v116.56h101.312C355.736,197.492,350.108,158.644,340.828,123.672z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M239.592,259.656h-100.04c0.708,42.74,6.34,81.588,15.62,116.556h84.424V259.656H239.592z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M255.132,259.656v116.556h85.696c9.28-34.964,14.912-73.816,15.616-116.556H255.132z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M239.592,395.636h-79.124c18.24,54.396,48.044,93.252,79.128,99.584v-99.584H239.592z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M255.132,395.636v99.584c34.968-6.328,62.156-45.188,80.392-99.584H255.132z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -26,6 +26,7 @@ Route::middleware('auth:api')->group(function () {
|
|||||||
'/meteo' => 'MeteoController',
|
'/meteo' => 'MeteoController',
|
||||||
'/to-do-lists' => 'ToDoListController',
|
'/to-do-lists' => 'ToDoListController',
|
||||||
'/to-do-lists/{toDoList}/to-do' => 'ToDoController',
|
'/to-do-lists/{toDoList}/to-do' => 'ToDoController',
|
||||||
|
'/bookmarks' => 'BookmarkController',
|
||||||
'/events/categories' => 'EventCategoryController',
|
'/events/categories' => 'EventCategoryController',
|
||||||
'/events' => 'EventController',
|
'/events' => 'EventController',
|
||||||
// '/users/{user}/posts' => 'UserPostController',
|
// '/users/{user}/posts' => 'UserPostController',
|
||||||
|
|||||||
215
tests/Feature/BookmarkTest.php
Normal file
215
tests/Feature/BookmarkTest.php
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Bookmark;
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class BookmarkTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function an_unauthenticated_user_can_add_a_bookmark()
|
||||||
|
{
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
|
$this->actingAs($user = factory(\App\User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$response = $this->post('/api/bookmarks', $this->data());
|
||||||
|
|
||||||
|
$bookmark = Bookmark::first();
|
||||||
|
|
||||||
|
$this->assertCount(1, Bookmark::all());
|
||||||
|
$this->assertEquals('Test Name', $bookmark->name);
|
||||||
|
$this->assertEquals('Test Description', $bookmark->description);
|
||||||
|
$this->assertEquals('https://portal.bricooli.fr', $bookmark->url);
|
||||||
|
// $this->assertEquals('https://portal.bricooli.fr/img/logo.svg', $bookmark->favicon);
|
||||||
|
|
||||||
|
$response->assertStatus(201);
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'type' => 'bookmark',
|
||||||
|
'bookmark_id' => $bookmark->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => $bookmark->name,
|
||||||
|
'description' => $bookmark->description,
|
||||||
|
'url' => $bookmark->url,
|
||||||
|
// 'favicon' => $bookmark->favicon,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function bookmark_url_are_required()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(\App\User::class)->create(), 'api');
|
||||||
|
$response = $this->post('/api/bookmarks', array_merge($this->data(), ['url' => '']));
|
||||||
|
|
||||||
|
$response->assertSessionHasErrors('url');
|
||||||
|
$this->assertCount(0, Bookmark::all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_bookmark_can_be_retrieved()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
$bookmark = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->get('/api/bookmarks/' . $bookmark->id );
|
||||||
|
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'bookmark_id' => $bookmark->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => $bookmark->name,
|
||||||
|
'url' => $bookmark->url,
|
||||||
|
'favicon' => $bookmark->favicon,
|
||||||
|
'last_updated' => $bookmark->updated_at->diffForHumans(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_user_can_retrueved_all_this_to_do_lists()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
$bookmarkOne = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
$bookmarkTwo = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->get('/api/bookmarks');
|
||||||
|
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
[
|
||||||
|
'data' => [
|
||||||
|
'bookmark_id' => $bookmarkOne->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => $bookmarkOne->name,
|
||||||
|
'last_updated' => $bookmarkOne->updated_at->diffForHumans(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'data' => [
|
||||||
|
'bookmark_id' => $bookmarkTwo->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => $bookmarkTwo->name,
|
||||||
|
'last_updated' => $bookmarkTwo->updated_at->diffForHumans(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'links' => [
|
||||||
|
'self' => url('/bookmarks'),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_owner_bookmark_can_retrieved_it()
|
||||||
|
{
|
||||||
|
$user = factory(User::class)->create();
|
||||||
|
$bookmark = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$this->actingAs($userAnother = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$response = $this->get('/api/bookmarks/' . $bookmark->id );
|
||||||
|
|
||||||
|
$response->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_to_bookmark_can_be_patch()
|
||||||
|
{
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
$bookmark = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->patch('/api/bookmarks/' . $bookmark->id, [
|
||||||
|
'name' => 'Bookmark Update',
|
||||||
|
'url' => 'https://portal.bricooli.fr',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$bookmark = $bookmark->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals('Bookmark Update', $bookmark->name);
|
||||||
|
$this->assertEquals('https://portal.bricooli.fr', $bookmark->url);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'bookmark_id' => $bookmark->id,
|
||||||
|
'attributes' => [
|
||||||
|
'data' => [
|
||||||
|
'name' => 'Bookmark Update'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_the_owner_can_patch_the_bookmark()
|
||||||
|
{
|
||||||
|
$user = factory(User::class)->create();
|
||||||
|
$bookmark = factory(Bookmark::class)->create(['id' => 123, 'user_id' => $user->id]);
|
||||||
|
|
||||||
|
$this->actingAs($anotherUser = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$this->patch('/api/bookmarks/'. $bookmark->id, ['url' => 'https://portal.bricooli.fr'])
|
||||||
|
->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_bookmark_can_be_delete()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$bookmark = factory(Bookmark::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->delete('/api/bookmarks/' . $bookmark->id);
|
||||||
|
|
||||||
|
$toDoList = $bookmark->fresh();
|
||||||
|
|
||||||
|
$this->assertCount(0, Bookmark::all());
|
||||||
|
|
||||||
|
$response->assertStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_the_owner_can_delete_the_bookmark()
|
||||||
|
{
|
||||||
|
$user = factory(User::class)->create();
|
||||||
|
$bookmark = factory(Bookmark::class)->create();
|
||||||
|
|
||||||
|
$this->actingAs($anotherUser = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$response = $this->delete('/api/bookmarks/' . $bookmark->id);
|
||||||
|
|
||||||
|
$response->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function data()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Test Name',
|
||||||
|
'description' => 'Test Description',
|
||||||
|
'url' => 'https://portal.bricooli.fr',
|
||||||
|
'favicon' => 'https://portal.bricooli.fr/img/logo.svg',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user