From: Louis Jeckel Date: Sun, 19 Apr 2020 12:26:53 +0000 (+0200) Subject: access logs, loigin with token, new Admin model X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=f11dd081abae3151434314dbc08228356b20d992;p=psq.git access logs, loigin with token, new Admin model --- diff --git a/app/AccessLog.php b/app/AccessLog.php new file mode 100644 index 0000000..7974728 --- /dev/null +++ b/app/AccessLog.php @@ -0,0 +1,48 @@ +belongsTo(User::class); + } + + + /** + * @param Request $request + */ + public static function log(Request $request): void + { + $entry = new self; + + $entry->ip = implode(', ', $request->ips()); + $entry->user_id = Auth::check() ? Auth::user()->id : null; + $entry->user_agent = $request->userAgent(); + + $entry->save(); + + } +} + diff --git a/app/Http/Controllers/FlowpaperController.php b/app/Http/Controllers/FlowpaperController.php index 91f0067..e36ff4f 100644 --- a/app/Http/Controllers/FlowpaperController.php +++ b/app/Http/Controllers/FlowpaperController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\AccessLog; use App\Flowpaper\Pdf2Json; use App\PdfFile; use Illuminate\Contracts\Filesystem\FileNotFoundException; @@ -12,14 +13,17 @@ use Illuminate\View\View; class FlowpaperController extends Controller { + /** * @param PdfFile $file + * @param Request $request * @return View * @throws \Illuminate\Auth\Access\AuthorizationException */ - public function view(PdfFile $file): View + public function view(PdfFile $file, Request $request): View { $this->authorize('view', $file); + AccessLog::log($request); return $file->view(); } diff --git a/app/Http/Controllers/PublishController.php b/app/Http/Controllers/PublishController.php index d8d4479..e8516ec 100644 --- a/app/Http/Controllers/PublishController.php +++ b/app/Http/Controllers/PublishController.php @@ -7,6 +7,7 @@ use App\FileTag; use App\Jobs\ProcessEmailBatch; use App\Jobs\ProcessPdfFile; use App\PdfFile; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Support\Arr; @@ -53,7 +54,11 @@ class PublishController extends Controller */ public function collections() { - return new ResourceCollection(FileCollection::all()); + $files = FileCollection::query()->with(['files' => function($builder) { + return $builder->orderByDesc('updated_at')->first(); + }])->get(); + + return new ResourceCollection($files); } public function recipientsCount() diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c3640f3..de5e1af 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,6 +2,8 @@ namespace App\Http; +use App\Http\Middleware\LoginWithToken; +use App\LoginToken; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel @@ -52,6 +54,7 @@ class Kernel extends HttpKernel * @var array */ protected $routeMiddleware = [ + 'login.token' => LoginWithToken::class, 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, diff --git a/app/Http/Middleware/LoginWithToken.php b/app/Http/Middleware/LoginWithToken.php new file mode 100644 index 0000000..af30cd4 --- /dev/null +++ b/app/Http/Middleware/LoginWithToken.php @@ -0,0 +1,23 @@ +get(); - $size = 2; + $size = env('MAILGUN_CHUNK_SIZE', 50); $chunks = $users->chunk($size); $this->processUpdate([ @@ -64,17 +64,17 @@ class ProcessEmailBatch implements ShouldQueue foreach($chunks as $chunk) { - $variables = json_encode($chunk->mapWithKeys(function($user) { + $variables = json_encode($chunk->mapWithKeys(function(User $user) { return [$user->email => [ 'id' => $user->id, 'name' => $user->name, + 'file_url' => $this->batch->file->getUrlWithToken($user), ]]; })); $view = view('emails.batch', [ 'subject' => $this->batch->subject, 'content' => $this->batch->content['body'], - 'link' => $this->batch->file->getUrl(), ])->render(); $params = [ diff --git a/app/LoginToken.php b/app/LoginToken.php new file mode 100644 index 0000000..0440562 --- /dev/null +++ b/app/LoginToken.php @@ -0,0 +1,81 @@ +where('valid_until', '>', now())->orWhereNull('valid_until'); + }); + } + + /** + * @param User $user + * @param Carbon|null $validUntil + * @return LoginToken + */ + public static function generateToken(User $user, ?Carbon $validUntil = null): LoginToken + { + $token = new self; + $token->user_id = $user->id; + $token->valid_until = $validUntil; + $token->token = \Str::random(128); + $token->save(); + + return $token; + + } + + + public static function checkAndLogin(Request $request): void + { + if(! $request->has('token')) { + return; + } + + $token = $request->get('token'); + + if($loginToken = self::where('token', $token)->first()) { + Auth::loginUsingId($loginToken->user_id); + $loginToken->delete(); + } + + } + + /** + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->token; + } +} diff --git a/app/Models/Admin.php b/app/Models/Admin.php new file mode 100644 index 0000000..7ca0408 --- /dev/null +++ b/app/Models/Admin.php @@ -0,0 +1,11 @@ +sortable(), Text::make('Nom', 'name'), HasMany::make('Membres', 'members', User::class), + Boolean::make('Abonnement actif', 'subscription_active') ]; } diff --git a/app/Nova/PdfFile.php b/app/Nova/PdfFile.php index 2e1b004..e3301ac 100644 --- a/app/Nova/PdfFile.php +++ b/app/Nova/PdfFile.php @@ -32,6 +32,8 @@ class PdfFile extends Resource 'title', ]; + + /** * Get the fields displayed by the resource. * diff --git a/app/Nova/SearchableText.php b/app/Nova/SearchableText.php index 074ee2c..16c87ff 100644 --- a/app/Nova/SearchableText.php +++ b/app/Nova/SearchableText.php @@ -31,6 +31,8 @@ class SearchableText extends Resource 'id', ]; + + /** * Get the fields displayed by the resource. * diff --git a/app/Nova/User.php b/app/Nova/User.php index 3167c32..17da525 100644 --- a/app/Nova/User.php +++ b/app/Nova/User.php @@ -4,9 +4,13 @@ namespace App\Nova; use Illuminate\Http\Request; use Laravel\Nova\Fields\BelongsTo; +use Laravel\Nova\Fields\Boolean; +use Laravel\Nova\Fields\Country; use Laravel\Nova\Fields\ID; +use Laravel\Nova\Fields\Place; use Laravel\Nova\Fields\Text; use Laravel\Nova\Http\Requests\NovaRequest; +use Laravel\Nova\Panel; class User extends Resource { @@ -22,16 +26,14 @@ class User extends Resource * * @var string */ - public static $title = 'id'; + public static $title = 'name'; - /** - * The columns that should be searched. - * - * @var array - */ - public static $search = [ - 'id', - ]; + public static $group = "CRM"; + + public static function label() + { + return "Abonnés"; + } /** * Get the fields displayed by the resource. @@ -42,13 +44,47 @@ class User extends Resource public function fields(Request $request) { return [ - ID::make()->sortable(), - Text::make('Nom', 'name'), - Text::make('Email'), - BelongsTo::make('Organisation', 'organization', Organization::class), + new Panel('Fiche', $this->basicInfo()), + new Panel('Adresse', $this->addressFields()), + new Panel('Affiliation', [ + BelongsTo::make('Organisation', 'organization', Organization::class), + + ]), + Boolean::make('Abonnement actif', 'isSubscribed')->readonly()->onlyOnIndex(), ]; } + + protected function basicInfo() + { + return [ + ID::make()->sortable()->onlyOnIndex(), + Text::make('Prénom', 'first_name'), + Text::make('Nom', 'last_name'), + Text::make('Email')->hideFromIndex(), + Text::make('Position'), + Text::make('Téléphone', 'phone'), + ]; + + } + + + /** + * Get the address fields for the resource. + * + * + */ + protected function addressFields() + { + return [ + Place::make('Address', 'address_line_1')->hideFromIndex(), + Text::make('City')->hideFromIndex(), + Text::make('Postal Code')->hideFromIndex(), + Country::make('Country')->hideFromIndex(), + ]; + } + + /** * Get the cards available for the request. * diff --git a/app/PdfFile.php b/app/PdfFile.php index 839749a..ea86e69 100644 --- a/app/PdfFile.php +++ b/app/PdfFile.php @@ -118,8 +118,10 @@ class PdfFile extends TwillModel implements Sortable $this->makeJson(); $this->makeCover(); $this->makeSearchable(); - $this->shortenLinks(); + if(!env('APP_ENV') === 'local') + $this->shortenLinks(); $this->saveToCloud(); + } @@ -272,7 +274,7 @@ class PdfFile extends TwillModel implements Sortable */ public function getCoverUrlAttribute(): string { - return Storage::cloud()->temporaryUrl($this->coverPath, now()->addDay()); + return Storage::cloud()->url($this->coverPath); } @@ -293,6 +295,11 @@ class PdfFile extends TwillModel implements Sortable return route('flowpaper.view', ['file' => $this->slug]); } + public function getUrlWithToken(User $user) + { + return $user->routeWithToken('flowpaper.view', ['file' => $this->slug]); + } + @@ -323,9 +330,13 @@ class PdfFile extends TwillModel implements Sortable Storage::disk('public')->makeDirectory('covers'); $pdf = new PdfToImage\Pdf($this->absolutePdfPath); $pdf->setResolution(72) - ->setCompressionQuality(60) ->saveImage($tmp); + $image =\Image::make($tmp); + $image->interlace(); + $image->save(null, 50); + + Storage::cloud()->putFileAS('/', $tmp, $this->coverPath); unlink($tmp); diff --git a/app/Policies/PdfFilePolicy.php b/app/Policies/PdfFilePolicy.php index 17ed76b..1e96858 100644 --- a/app/Policies/PdfFilePolicy.php +++ b/app/Policies/PdfFilePolicy.php @@ -34,9 +34,9 @@ class PdfFilePolicy * @return mixed * @throws AuthenticationException */ - public function view($user, PdfFile $pdfFile) + public function view($user = null, PdfFile $pdfFile) { - if($pdfFile->is_free || ($user instanceof \App\User ? $user->isSubscribed() : false)) { + if($pdfFile->is_free || ($user instanceof \App\User ? $user->isSubscribed : false)) { return true; } diff --git a/app/SearchableText.php b/app/SearchableText.php index 9bba349..d5439e2 100644 --- a/app/SearchableText.php +++ b/app/SearchableText.php @@ -26,7 +26,8 @@ class SearchableText extends Model 'slug' => $this->file->slug, 'tags' => $this->file->fileTags()->pluck('content')->toArray(), 'collection' => (string) $this->file->collection, - 'ref' => $this->file->ref + 'ref' => $this->file->ref, + 'cover' => $this->file->coverUrl, ], 'content' => $this->content, 'page' => $this->page, diff --git a/app/User.php b/app/User.php index a0083cc..e6d46b7 100644 --- a/app/User.php +++ b/app/User.php @@ -7,18 +7,23 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Scout\Searchable; /** * Class User * @package App * @property $first_name * @property $last_name + * @property $id * @property-read $name * @property Organization $organization + * @property string $position + * @property bool $isSubscribed */ class User extends Authenticatable { use Notifiable; + use Searchable; /** * The attributes that are mass assignable. @@ -48,6 +53,15 @@ class User extends Authenticatable 'subscription_active' => 'boolean', ]; + public function toSearchableArray() + { + return [ + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, + 'position' => $this->position, + 'organization' => $this->organization->name, + ]; + } /** * @return BelongsTo @@ -60,7 +74,7 @@ class User extends Authenticatable /** * @return bool */ - public function isSubscribed(): bool + public function getIsSubscribedAttribute(): bool { if($o = $this->organization){ return $o->isSubscribed(); @@ -69,6 +83,17 @@ class User extends Authenticatable return false; } + public function loginTokens() + { + return $this->hasMany(LoginToken::class); + } + + public function routeWithToken($route, $params = [], $absolute = true) + { + $token = [ 'token' => LoginToken::generateToken($this)->token ]; + return route($route, array_merge($params, $token), $absolute); + } + /** * @param Builder $builder */ @@ -95,4 +120,7 @@ class User extends Authenticatable + + + } diff --git a/config/app.php b/config/app.php index 0de210c..e237267 100644 --- a/config/app.php +++ b/config/app.php @@ -80,7 +80,7 @@ return [ | */ - 'locale' => 'en', + 'locale' => 'fr', /* |-------------------------------------------------------------------------- diff --git a/config/auth.php b/config/auth.php index d8a95c8..fe97364 100644 --- a/config/auth.php +++ b/config/auth.php @@ -78,7 +78,7 @@ return [ 'admins' => [ 'driver' => 'eloquent', - 'model' => \A17\Twill\Models\User::class, + 'model' => \App\Models\Admin::class, ], ], diff --git a/config/twill.php b/config/twill.php index 0252030..16649dc 100644 --- a/config/twill.php +++ b/config/twill.php @@ -194,6 +194,6 @@ return [ |-------------------------------------------------------------------------- | */ - 'locale' => 'en', + 'locale' => 'fr', 'fallback_locale' => 'en', ]; diff --git a/database/migrations/2020_04_14_161920_create_access_logs_table.php b/database/migrations/2020_04_14_161920_create_access_logs_table.php new file mode 100644 index 0000000..a8f4655 --- /dev/null +++ b/database/migrations/2020_04_14_161920_create_access_logs_table.php @@ -0,0 +1,34 @@ +id(); + $table->timestamps(); + $table->string('ip'); + $table->string('user_agent'); + $table->unsignedBigInteger('user_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('access_logs'); + } +} diff --git a/database/migrations/2020_04_15_124420_create_access_tokens_table.php b/database/migrations/2020_04_15_124420_create_access_tokens_table.php new file mode 100644 index 0000000..38d84eb --- /dev/null +++ b/database/migrations/2020_04_15_124420_create_access_tokens_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('token')->unique(); + $table->unsignedBigInteger('user_id'); + $table->dateTime('valid_until')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('login_tokens'); + } +} diff --git a/database/migrations/2020_04_19_115243_user_place_update_to_users.php b/database/migrations/2020_04_19_115243_user_place_update_to_users.php new file mode 100644 index 0000000..df5faa9 --- /dev/null +++ b/database/migrations/2020_04_19_115243_user_place_update_to_users.php @@ -0,0 +1,35 @@ +renameColumn('city_name', 'city'); + $table->renameColumn('zip_code', 'postal_code'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->renameColumn('city', 'city_name'); + $table->renameColumn('postal_code', 'zip_code'); + }); + } +} diff --git a/public/css/app.css b/public/css/app.css index 559cf39..c92b75b 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -2147,7 +2147,8 @@ pre code { border: 0; } -.form-control { +.form-control, +.ais-SearchBox-input { display: block; width: 100%; height: calc(1.6em + 0.75rem + 2px); @@ -2164,22 +2165,26 @@ pre code { } @media (prefers-reduced-motion: reduce) { - .form-control { + .form-control, + .ais-SearchBox-input { transition: none; } } -.form-control::-ms-expand { +.form-control::-ms-expand, +.ais-SearchBox-input::-ms-expand { background-color: transparent; border: 0; } -.form-control:-moz-focusring { +.form-control:-moz-focusring, +.ais-SearchBox-input:-moz-focusring { color: transparent; text-shadow: 0 0 0 #495057; } -.form-control:focus { +.form-control:focus, +.ais-SearchBox-input:focus { color: #495057; background-color: #fff; border-color: #a1cbef; @@ -2187,38 +2192,42 @@ pre code { box-shadow: 0 0 0 0.2rem rgba(52, 144, 220, 0.25); } -.form-control::-webkit-input-placeholder { +.form-control::-webkit-input-placeholder, .ais-SearchBox-input::-webkit-input-placeholder { color: #6c757d; opacity: 1; } -.form-control::-moz-placeholder { +.form-control::-moz-placeholder, .ais-SearchBox-input::-moz-placeholder { color: #6c757d; opacity: 1; } -.form-control:-ms-input-placeholder { +.form-control:-ms-input-placeholder, .ais-SearchBox-input:-ms-input-placeholder { color: #6c757d; opacity: 1; } -.form-control::-ms-input-placeholder { +.form-control::-ms-input-placeholder, .ais-SearchBox-input::-ms-input-placeholder { color: #6c757d; opacity: 1; } -.form-control::placeholder { +.form-control::placeholder, +.ais-SearchBox-input::placeholder { color: #6c757d; opacity: 1; } .form-control:disabled, -.form-control[readonly] { +.ais-SearchBox-input:disabled, +.form-control[readonly], +[readonly].ais-SearchBox-input { background-color: #e9ecef; opacity: 1; } -select.form-control:focus::-ms-value { +select.form-control:focus::-ms-value, +select.ais-SearchBox-input:focus::-ms-value { color: #495057; background-color: #fff; } @@ -2287,11 +2296,14 @@ select.form-control:focus::-ms-value { } select.form-control[size], -select.form-control[multiple] { +select[size].ais-SearchBox-input, +select.form-control[multiple], +select[multiple].ais-SearchBox-input { height: auto; } -textarea.form-control { +textarea.form-control, +textarea.ais-SearchBox-input { height: auto; } @@ -2383,7 +2395,9 @@ textarea.form-control { } .was-validated .form-control:valid, -.form-control.is-valid { +.was-validated .ais-SearchBox-input:valid, +.form-control.is-valid, +.is-valid.ais-SearchBox-input { border-color: #38c172; padding-right: calc(1.6em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2338c172' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); @@ -2393,13 +2407,17 @@ textarea.form-control { } .was-validated .form-control:valid:focus, -.form-control.is-valid:focus { +.was-validated .ais-SearchBox-input:valid:focus, +.form-control.is-valid:focus, +.is-valid.ais-SearchBox-input:focus { border-color: #38c172; box-shadow: 0 0 0 0.2rem rgba(56, 193, 114, 0.25); } .was-validated textarea.form-control:valid, -textarea.form-control.is-valid { +.was-validated textarea.ais-SearchBox-input:valid, +textarea.form-control.is-valid, +textarea.is-valid.ais-SearchBox-input { padding-right: calc(1.6em + 0.75rem); background-position: top calc(0.4em + 0.1875rem) right calc(0.4em + 0.1875rem); } @@ -2497,7 +2515,9 @@ textarea.form-control.is-valid { } .was-validated .form-control:invalid, -.form-control.is-invalid { +.was-validated .ais-SearchBox-input:invalid, +.form-control.is-invalid, +.is-invalid.ais-SearchBox-input { border-color: #e3342f; padding-right: calc(1.6em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23e3342f' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23e3342f' stroke='none'/%3e%3c/svg%3e"); @@ -2507,13 +2527,17 @@ textarea.form-control.is-valid { } .was-validated .form-control:invalid:focus, -.form-control.is-invalid:focus { +.was-validated .ais-SearchBox-input:invalid:focus, +.form-control.is-invalid:focus, +.is-invalid.ais-SearchBox-input:focus { border-color: #e3342f; box-shadow: 0 0 0 0.2rem rgba(227, 52, 47, 0.25); } .was-validated textarea.form-control:invalid, -textarea.form-control.is-invalid { +.was-validated textarea.ais-SearchBox-input:invalid, +textarea.form-control.is-invalid, +textarea.is-invalid.ais-SearchBox-input { padding-right: calc(1.6em + 0.75rem); background-position: top calc(0.4em + 0.1875rem) right calc(0.4em + 0.1875rem); } @@ -2606,7 +2630,8 @@ textarea.form-control.is-invalid { margin-bottom: 0; } - .form-inline .form-control { + .form-inline .form-control, + .form-inline .ais-SearchBox-input { display: inline-block; width: auto; vertical-align: middle; @@ -3785,6 +3810,7 @@ input[type=button].btn-block { } .input-group > .form-control, +.input-group > .ais-SearchBox-input, .input-group > .form-control-plaintext, .input-group > .custom-select, .input-group > .custom-file { @@ -3795,21 +3821,30 @@ input[type=button].btn-block { } .input-group > .form-control + .form-control, +.input-group > .ais-SearchBox-input + .form-control, +.input-group > .form-control + .ais-SearchBox-input, +.input-group > .ais-SearchBox-input + .ais-SearchBox-input, .input-group > .form-control + .custom-select, +.input-group > .ais-SearchBox-input + .custom-select, .input-group > .form-control + .custom-file, +.input-group > .ais-SearchBox-input + .custom-file, .input-group > .form-control-plaintext + .form-control, +.input-group > .form-control-plaintext + .ais-SearchBox-input, .input-group > .form-control-plaintext + .custom-select, .input-group > .form-control-plaintext + .custom-file, .input-group > .custom-select + .form-control, +.input-group > .custom-select + .ais-SearchBox-input, .input-group > .custom-select + .custom-select, .input-group > .custom-select + .custom-file, .input-group > .custom-file + .form-control, +.input-group > .custom-file + .ais-SearchBox-input, .input-group > .custom-file + .custom-select, .input-group > .custom-file + .custom-file { margin-left: -1px; } .input-group > .form-control:focus, +.input-group > .ais-SearchBox-input:focus, .input-group > .custom-select:focus, .input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { z-index: 3; @@ -3820,12 +3855,14 @@ input[type=button].btn-block { } .input-group > .form-control:not(:last-child), +.input-group > .ais-SearchBox-input:not(:last-child), .input-group > .custom-select:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group > .form-control:not(:first-child), +.input-group > .ais-SearchBox-input:not(:first-child), .input-group > .custom-select:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; @@ -3904,11 +3941,13 @@ input[type=button].btn-block { } .input-group-lg > .form-control:not(textarea), +.input-group-lg > .ais-SearchBox-input:not(textarea), .input-group-lg > .custom-select { height: calc(1.5em + 1rem + 2px); } .input-group-lg > .form-control, +.input-group-lg > .ais-SearchBox-input, .input-group-lg > .custom-select, .input-group-lg > .input-group-prepend > .input-group-text, .input-group-lg > .input-group-append > .input-group-text, @@ -3921,11 +3960,13 @@ input[type=button].btn-block { } .input-group-sm > .form-control:not(textarea), +.input-group-sm > .ais-SearchBox-input:not(textarea), .input-group-sm > .custom-select { height: calc(1.5em + 0.5rem + 2px); } .input-group-sm > .form-control, +.input-group-sm > .ais-SearchBox-input, .input-group-sm > .custom-select, .input-group-sm > .input-group-prepend > .input-group-text, .input-group-sm > .input-group-append > .input-group-text, @@ -10922,3 +10963,14 @@ a.text-dark:focus { -webkit-animation: blink-fade 1000ms infinite; } +.ais-SearchBox-input { + width: calc(100% - 20px); + display: inline-block; +} + +mark.ais-Snippet-highlighted, +mark.mark { + padding: 0; + background-color: #fff252; +} + diff --git a/public/js/app.js b/public/js/app.js index e01b68c..35072fa 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -7360,6 +7360,17 @@ __webpack_require__.r(__webpack_exports__); // // // +// +// +// +// +// +// +// +// +// +// +// /* harmony default export */ __webpack_exports__["default"] = ({ name: "FileHit", props: ['hit'], @@ -7367,11 +7378,8 @@ __webpack_require__.r(__webpack_exports__); return {}; }, computed: { - imgLink: function imgLink() { - return "/files/".concat(this.hit.file.slug, "/cover"); - }, viewLink: function viewLink() { - return "/view/".concat(this.hit.file.slug); + return "/view/".concat(this.hit.file.slug, "#page=").concat(this.hit.page); } } }); @@ -7438,6 +7446,9 @@ __webpack_require__.r(__webpack_exports__); // // // +// +// +// /* harmony default export */ __webpack_exports__["default"] = ({ mixins: [_mixins_SearchMixin__WEBPACK_IMPORTED_MODULE_0__["default"]], @@ -7770,7 +7781,6 @@ __webpack_require__.r(__webpack_exports__); // // // -// /* harmony default export */ __webpack_exports__["default"] = ({ @@ -7795,6 +7805,9 @@ __webpack_require__.r(__webpack_exports__); uploadedFile: function uploadedFile() { return this.files.length > 0 ? this.files[0] : null; }, + nextRef: function nextRef() { + return this.getNextRef(this.file_collection); + }, fileValid: function fileValid() { if (this.files.length < 1) return; return this.checkFileValid(this.files[0]); @@ -7838,6 +7851,16 @@ __webpack_require__.r(__webpack_exports__); }); } }, + getNextRef: function getNextRef(collection_id) { + var collection = this.collections.find(function (c) { + return c.id === collection_id; + }); + if (collection === undefined) return null; + var lastRef = collection.files[0].ref; + return lastRef.replace(/(\d+)+/g, function (match, number) { + return parseInt(number) + 1; + }); + }, checkFileValid: function checkFileValid(file) { return file.type === 'application/pdf'; }, @@ -7856,7 +7879,8 @@ __webpack_require__.r(__webpack_exports__); var _this = this; axios.get('/publish/collections').then(function (d) { - return _this.collections = d.data.data; + _this.collections = d.data.data; + _this.file_ref = _this.getNextRef(1); }); axios.get('/publish/tags').then(function (d) { var tags = d.data.data; @@ -7951,7 +7975,7 @@ __webpack_require__.r(__webpack_exports__); file: this.$root.publishState.file, editor: _ckeditor_ckeditor5_build_classic__WEBPACK_IMPORTED_MODULE_0___default.a, email: { - content: "\n

Bonjour $nom$,

\n

Voici la nouvelle \xE9dition du jour !

\n ", + content: "\n

Bonjour %recipient.name%,

\n

Voici la nouvelle \xE9dition du jour !

\n ", subject: "[".concat(this.$root.publishState.file.title, "] ") }, csrf: document.querySelectorAll('meta[name="csrf-token"]')[0].content, @@ -78788,31 +78812,61 @@ var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h - return _c("div", { staticClass: "card w-100" }, [ - _c("img", { - staticClass: "card-img-top", - attrs: { src: _vm.imgLink, alt: "Cover" } - }), - _vm._v(" "), - _c("div", { staticClass: "card-body" }, [ - _c("h5", { staticClass: "card-title" }, [ - _vm._v(_vm._s(_vm.hit.file.collection) + " - "), - _c("strong", [_vm._v(_vm._s(_vm.hit.file.ref))]) + return _c( + "div", + { staticClass: "card w-100" }, + [ + _c("img", { + staticClass: "card-img-top", + attrs: { src: _vm.hit.file.cover, alt: "Cover" } + }), + _vm._v(" "), + _c("div", { staticClass: "card-body" }, [ + _c("h5", { staticClass: "card-title" }, [ + _vm._v(_vm._s(_vm.hit.file.collection) + " - "), + _c("strong", [_vm._v(_vm._s(_vm.hit.file.ref))]) + ]), + _vm._v(" "), + _c( + "p", + { staticClass: "card-text" }, + _vm._l(_vm.hit.file.tags, function(tag) { + return _c("span", { staticClass: "badge badge-light mr-1" }, [ + _c("i", { staticClass: "fas fa-tag" }), + _vm._v(" " + _vm._s(tag) + "\n ") + ]) + }), + 0 + ), + _vm._v(" "), + _c( + "p", + { staticClass: "card-text" }, + [ + _vm.hit._highlightResult.content.matchLevel !== "none" + ? _c("ais-snippet", { + attrs: { attribute: "content", hit: _vm.hit } + }) + : _vm._e() + ], + 1 + ) ]), _vm._v(" "), _c( - "p", - { staticClass: "card-text" }, - [_c("ais-snippet", { attrs: { attribute: "content", hit: _vm.hit } })], - 1 + "md-button", + { + staticClass: "md-raised ", + attrs: { href: _vm.viewLink, target: "_blank" } + }, + [ + _c("i", { staticClass: "fas fa-book-open" }), + _vm._v(" Lire cette édition") + ] ) - ]), - _vm._v(" "), - _c("a", { staticClass: "btn btn-primary", attrs: { href: _vm.viewLink } }, [ - _c("i", { staticClass: "fas fa-book-open" }), - _vm._v(" Lire cette édition") - ]) - ]) + ], + 1 + ) } var staticRenderFns = [] render._withStripped = true @@ -78855,12 +78909,19 @@ var render = function() { "div", { staticClass: "col-md-12" }, [ - _c("ais-search-box", { - staticClass: "searchbox", - attrs: { placeholder: "Rechercher..." } - }), + _c( + "div", + { staticClass: "px-3" }, + [ + _c("ais-search-box", { + staticClass: "searchbox ", + attrs: { placeholder: "Rechercher..." } + }) + ], + 1 + ), _vm._v(" "), - _c("ais-stats"), + _c("ais-stats", { staticClass: "mt-2 ml-3" }), _vm._v(" "), _c( "div", @@ -79136,9 +79197,12 @@ var render = function() { } }, _vm._l(_vm.collections, function(collection) { - return _c("option", { domProps: { value: collection.id } }, [ - _vm._v(_vm._s(collection.name)) - ]) + return _c("option", { + domProps: { + value: collection.id, + textContent: _vm._s(collection.name) + } + }) }), 0 ) @@ -79158,11 +79222,11 @@ var render = function() { expression: "file_ref" } ], - staticClass: "form-control is-invalid", + staticClass: "form-control is-valid", attrs: { type: "email", id: "file_ref", - placeholder: "ex: 482", + placeholder: _vm.nextRef, required: "" }, domProps: { value: _vm.file_ref }, @@ -79239,9 +79303,7 @@ var render = function() { }, [ _c("i", { staticClass: "fa fa-plus" }), - _vm._v( - "\n Sélectionnez un fichier\n " - ) + _vm._v("\n Sélectionnez un fichier\n ") ] ) ], @@ -79355,7 +79417,7 @@ var render = function() { }, [ _c("i", { staticClass: "fas fa-check" }), - _vm._v(" \n "), + _vm._v(" \n "), _c("span", [ _c("strong", [ _vm._v( @@ -95013,6 +95075,7 @@ window.Vue = __webpack_require__(/*! vue */ "./node_modules/vue/dist/vue.common. window.Vue.use(_ckeditor_ckeditor5_vue__WEBPACK_IMPORTED_MODULE_5___default.a); window.Vue.use(vue_instantsearch__WEBPACK_IMPORTED_MODULE_6__["default"]); window.Vue.use(vue_material_dist_components__WEBPACK_IMPORTED_MODULE_8__["MdProgress"]); +window.Vue.use(vue_material_dist_components__WEBPACK_IMPORTED_MODULE_8__["MdBadge"]); window.Vue.use(vue_material_dist_components__WEBPACK_IMPORTED_MODULE_8__["MdButton"]); /** * The following block of code may be used to automatically register your diff --git a/resources/js/app.js b/resources/js/app.js index d680152..ebd4c27 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -14,11 +14,12 @@ import '@fortawesome/fontawesome-free/css/all.min.css'; import CKEditor from '@ckeditor/ckeditor5-vue'; import InstantSearch from "vue-instantsearch"; import 'instantsearch.css/themes/reset-min.css'; -import { MdProgress, MdButton } from 'vue-material/dist/components' +import { MdProgress, MdButton, MdBadge } from 'vue-material/dist/components' window.Vue.use( CKEditor ); window.Vue.use(InstantSearch); window.Vue.use(MdProgress); +window.Vue.use(MdBadge); window.Vue.use(MdButton); /** diff --git a/resources/js/components/FileSearch/FileHit.vue b/resources/js/components/FileSearch/FileHit.vue index 7a599b9..da49c14 100644 --- a/resources/js/components/FileSearch/FileHit.vue +++ b/resources/js/components/FileSearch/FileHit.vue @@ -1,12 +1,23 @@ -