mirror of https://github.com/MarceauKa/shaark.git
✨ Multi-users
This commit is contained in:
parent
603eec7fe8
commit
c678694de3
|
@ -25,7 +25,7 @@ class ChestController extends Controller
|
|||
'content',
|
||||
])->toArray());
|
||||
|
||||
$post = new Post(['is_private' => true]);
|
||||
$post = new Post(['is_private' => true, 'user_id' => $request->user()->id]);
|
||||
$post->postable()->associate($chest)->save();
|
||||
|
||||
if ($data['tags']) {
|
||||
|
|
|
@ -58,6 +58,7 @@ class LinkController extends Controller
|
|||
|
||||
$post = new Post();
|
||||
$post->is_private = $data->get('is_private', 0);
|
||||
$post->user_id = $request->user()->id;
|
||||
$post->postable()->associate($link)->save();
|
||||
|
||||
if ($data['tags']) {
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\Manage;
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Manage\StoreUserRequest;
|
||||
use App\Http\Requests\Manage\UpdateUserRequest;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Post;
|
||||
use App\User;
|
||||
|
@ -37,6 +38,28 @@ class UsersController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateUserRequest $request, int $id)
|
||||
{
|
||||
$validated = collect($request->validated());
|
||||
$user = User::findOrFail($id);
|
||||
|
||||
$user->fill($validated->only('name', 'email')->toArray());
|
||||
|
||||
$user->is_admin = $validated->get('is_admin', 0) == 1;
|
||||
|
||||
if ($validated->get('password')) {
|
||||
$user->password = Hash::make($validated->get('password'));
|
||||
$user->api_token = User::generateApiToken();
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'updated',
|
||||
'id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(Request $request, int $id)
|
||||
{
|
||||
if ($request->user()->id == $id) {
|
||||
|
@ -49,7 +72,13 @@ class UsersController extends Controller
|
|||
$user = User::findOrFail($id);
|
||||
$user->delete();
|
||||
|
||||
Post::where('user_id', $id)->delete();
|
||||
$admin = User::isAdmin()->first();
|
||||
|
||||
if ($admin) {
|
||||
Post::where('user_id', $id)->update('user_id', $admin->id);
|
||||
} else {
|
||||
Post::where('user_id', $id)->update('user_id', null);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'deleted',
|
||||
|
|
|
@ -28,6 +28,7 @@ class StoryController extends Controller
|
|||
|
||||
$post = new Post();
|
||||
$post->is_private = $data->get('is_private', 0);
|
||||
$post->user_id = $request->user()->id;
|
||||
$post->postable()->associate($story)->save();
|
||||
|
||||
if ($data['tags']) {
|
||||
|
|
|
@ -8,11 +8,13 @@ use App\Post;
|
|||
use App\Story;
|
||||
use App\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BrowseController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
DB::enableQueryLog();
|
||||
if (true === app('shaarli')->getHomepageAlt()) {
|
||||
$posts = Post::with('tags', 'postable')
|
||||
->withoutChests()
|
||||
|
@ -35,7 +37,7 @@ class BrowseController extends Controller
|
|||
->withPrivate($request)
|
||||
->latest()
|
||||
->paginate(20);
|
||||
|
||||
//dd(DB::getQueryLog());
|
||||
return view('home')->with([
|
||||
'page_title' => app('shaarli')->getName(),
|
||||
'posts' => $posts,
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Manage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateUserRequest extends FormRequest
|
||||
{
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() && auth()->user()->is_admin;
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
'min:2',
|
||||
'max:255',
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
'email',
|
||||
Rule::unique('users', 'email')->ignore($this->route('id'), 'id'),
|
||||
],
|
||||
'password' => [
|
||||
'nullable',
|
||||
'confirmed',
|
||||
'min:8',
|
||||
]
|
||||
];
|
||||
|
||||
if (auth()->user()->id == $this->route('id')) {
|
||||
$rules['is_admin'] = [
|
||||
'required',
|
||||
'in:1'
|
||||
];
|
||||
} else {
|
||||
$rules['is_admin'] = [
|
||||
'nullable',
|
||||
];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
29
app/Post.php
29
app/Post.php
|
@ -5,6 +5,7 @@ namespace App;
|
|||
use App\Concerns\Models\HasTags;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
@ -22,6 +23,7 @@ class Post extends Model
|
|||
'postable_type',
|
||||
'postable_id',
|
||||
'is_private',
|
||||
'user_id',
|
||||
'created_at',
|
||||
];
|
||||
protected $casts = [
|
||||
|
@ -33,6 +35,11 @@ class Post extends Model
|
|||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function getCreatedAtFormatedAttribute(): string
|
||||
{
|
||||
return $this->created_at->diffForHumans();
|
||||
|
@ -40,18 +47,28 @@ class Post extends Model
|
|||
|
||||
public function scopeWithPrivate(Builder $query, $private = false): Builder
|
||||
{
|
||||
$user = null;
|
||||
|
||||
if ($private instanceof Request) {
|
||||
$private = $private->user() instanceof User;
|
||||
$user = $private->user();
|
||||
} else if ($private instanceof User) {
|
||||
$user = $private;
|
||||
}
|
||||
|
||||
if ($private instanceof User) {
|
||||
$private = true;
|
||||
}
|
||||
|
||||
if ($private === false) {
|
||||
if (! $user) {
|
||||
return $query->where('is_private', 0);
|
||||
}
|
||||
|
||||
if ($user->is_admin === false) {
|
||||
return $query
|
||||
->where('is_private', 0)
|
||||
->orWhere(function ($query) use ($user) {
|
||||
return $query
|
||||
->where('is_private', 1)
|
||||
->where('user_id', $user->id);
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
|
12
app/User.php
12
app/User.php
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Str;
|
||||
|
@ -31,6 +33,16 @@ class User extends Authenticatable implements HasLoginsAndDevicesInterface
|
|||
'is_admin' => 'bool',
|
||||
];
|
||||
|
||||
public function posts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Post::class);
|
||||
}
|
||||
|
||||
public function scopeIsAdmin(Builder $query): Builder
|
||||
{
|
||||
return $query->where('is_admin', 1);
|
||||
}
|
||||
|
||||
public static function generateApiToken(): string
|
||||
{
|
||||
return Str::random(128);
|
||||
|
|
|
@ -54,7 +54,7 @@ class DatabaseSeeder extends Seeder
|
|||
];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$post = new App\Post(['is_private' => get_class($item) === Chest::class]);
|
||||
$post = new App\Post(['is_private' => get_class($item) === Chest::class, 'user_id' => 1]);
|
||||
$item->post()->save($post);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"/js/app.js": "/js/app.js?id=742ab0f0dd07e1506a39",
|
||||
"/js/app.js": "/js/app.js?id=290226c7dec36f245097",
|
||||
"/css/app.css": "/css/app.css?id=3716ce4aecaaa0a26ddd"
|
||||
}
|
||||
|
|
12
readme.md
12
readme.md
|
@ -45,8 +45,9 @@ but built with [Laravel](https://github.com/laravel/laravel) and [Vue.js](https:
|
|||
- [x] Export
|
||||
- [x] Dark mode
|
||||
- [x] i18n (english and french)
|
||||
- [x] **NEW** Archiving
|
||||
- [x] **NEW** 2-FA with email
|
||||
- [x] Archiving
|
||||
- [x] 2-FA with email
|
||||
- [x] **NEW** Multi-users
|
||||
- [ ] RSS feed
|
||||
|
||||
## Demo
|
||||
|
@ -103,7 +104,12 @@ Shaarli logs all successful and failed auths with their associated devices.
|
|||
|
||||
### Chests encryption
|
||||
|
||||
Since `1.2.9`, all chests data are encrypted in your database using AES-256-CBC and your app key.
|
||||
Since `1.2.9`, all chests data are encrypted in your database using AES-256-CBC and your app key.
|
||||
|
||||
### Multi-users
|
||||
|
||||
Others users can be admin or non-admin. Admin users are like the main user and have an access to the entire content.
|
||||
Non-admin users can't access the settings section and can only see their own private content.
|
||||
|
||||
## Update
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="! user">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="password">{{ __('Password') }}</label>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<label class="custom-control-label" for="is_admin">{{ __('Is admin?') }}</label>
|
||||
</div>
|
||||
|
||||
<span class="text-muted">{{ __("Admin users can access settings") }}</span>
|
||||
<span class="text-muted">{{ __("Admin users can access settings and other users private content") }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" @click.prevent="submit" :disabled="loading">
|
||||
|
|
|
@ -191,7 +191,7 @@
|
|||
"Add user": "Ajouter utilisateur",
|
||||
"Update user": "Modifier utilisateur",
|
||||
"Is admin?": "Administrateur ?",
|
||||
"Admin users can access settings": "Les utilisateurs admin peuvent accéder aux paramètres",
|
||||
"Admin users can access settings and other users private content": "Les utilisateurs admin peuvent accéder aux paramètres et contenu privé des autres utilisateurs",
|
||||
"Unable to load users": "Impossible de charger les utilisateurs",
|
||||
"Unable to save user": "Impossible d'enregistrer l'utilisateur",
|
||||
"User created": "L'utilisateur a été créé",
|
||||
|
|
Loading…
Reference in New Issue