]> _ Git - psq.git/commitdiff
access logs, loigin with token, new Admin model
authorLouis Jeckel <louis.jeckel@outlook.cm>
Sun, 19 Apr 2020 12:26:53 +0000 (14:26 +0200)
committerLouis Jeckel <louis.jeckel@outlook.cm>
Sun, 19 Apr 2020 12:26:53 +0000 (14:26 +0200)
35 files changed:
app/AccessLog.php [new file with mode: 0644]
app/Http/Controllers/FlowpaperController.php
app/Http/Controllers/PublishController.php
app/Http/Kernel.php
app/Http/Middleware/LoginWithToken.php [new file with mode: 0644]
app/Jobs/ProcessEmailBatch.php
app/LoginToken.php [new file with mode: 0644]
app/Models/Admin.php [new file with mode: 0644]
app/Models/User.php [deleted file]
app/Nova/Admin.php
app/Nova/EmailBatch.php
app/Nova/Organization.php
app/Nova/PdfFile.php
app/Nova/SearchableText.php
app/Nova/User.php
app/PdfFile.php
app/Policies/PdfFilePolicy.php
app/SearchableText.php
app/User.php
config/app.php
config/auth.php
config/twill.php
database/migrations/2020_04_14_161920_create_access_logs_table.php [new file with mode: 0644]
database/migrations/2020_04_15_124420_create_access_tokens_table.php [new file with mode: 0644]
database/migrations/2020_04_19_115243_user_place_update_to_users.php [new file with mode: 0644]
public/css/app.css
public/js/app.js
resources/js/app.js
resources/js/components/FileSearch/FileHit.vue
resources/js/components/FileSearch/FileInstantSearch.vue
resources/js/components/Publish/Step1UploadFile.vue
resources/js/components/Publish/Step2PrepareMail.vue
resources/sass/app.scss
resources/views/emails/batch.blade.php
routes/web.php

diff --git a/app/AccessLog.php b/app/AccessLog.php
new file mode 100644 (file)
index 0000000..7974728
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+namespace App;
+
+use Auth;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations;
+use Illuminate\Http\Request;
+
+/**
+ * Class AccessLog
+ * @package App
+ * @property string $ip;
+ * @property string $user_agent;
+ * @property User $user;
+ * @property int $user_id;
+ *
+ */
+class AccessLog extends Model
+{
+    protected $guarded = [];
+
+
+    /**
+     * @return Relations\BelongsTo
+     */
+    public function user(): Relations\BelongsTo
+    {
+        return $this->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();
+
+    }
+}
+
index 91f0067b30c9a79917e310b82e6a6272c46d164a..e36ff4f5e1e1e68586b9ec8e295f7a8228dc09d7 100644 (file)
@@ -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();
     }
 
index d8d44793fa9311e80b6f2727ef4d0e174865d389..e8516ec0635097327b79ff97b2f431b9f96e50af 100644 (file)
@@ -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()
index c3640f30b87df218bf607ab76eb06ee4601b7f5c..de5e1af7c702e0948cbe5cef1de8e9878a2ded27 100644 (file)
@@ -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 (file)
index 0000000..af30cd4
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use App\LoginToken;
+use Closure;
+
+class LoginWithToken
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        LoginToken::checkAndLogin($request);
+
+        return $next($request);
+    }
+}
index 59e0a0ce05397713deb7d8618308ca1f467e2d39..76a6449bdaf7436fefdd2575d156d96289768a18 100644 (file)
@@ -51,7 +51,7 @@ class ProcessEmailBatch implements ShouldQueue
         /** @var Collection $users */
         $users = User::subscribed()->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 (file)
index 0000000..0440562
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+namespace App;
+
+use Auth;
+use Carbon\Carbon;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Http\Request;
+
+/**
+ * Class LoginToken
+ * @package App
+ * @property string $token
+ * @property int $user_id
+ * @property User $user
+ * @property Carbon $valid_until
+ */
+class LoginToken extends Model
+{
+
+    /**
+     *
+     */
+    protected static function booted()
+    {
+        static::addGlobalScope('valid', function (Builder $builder) {
+            $builder->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 (file)
index 0000000..7ca0408
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Models;
+
+use A17\Twill\Models\User;
+
+class Admin extends User
+{
+
+
+}
diff --git a/app/Models/User.php b/app/Models/User.php
deleted file mode 100644 (file)
index 36b8477..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-namespace App\Models;
-
-
-use A17\Twill\Models\Model;
-
-class User extends Model
-{
-
-
-
-    protected $fillable = [
-        'published',
-        'title',
-        'description',
-    ];
-
-}
index ef84e9b76492cc89a0038eae6412eadec6be82dd..b2de57406ac007a11c2932fe82e01e1cc8f8a5e9 100644 (file)
@@ -15,7 +15,7 @@ class Admin extends Resource
      *
      * @var string
      */
-    public static $model = \A17\Twill\Models\User::class;
+    public static $model = \App\Models\Admin::class;
 
     /**
      * The single value that should be used to represent the resource when being displayed.
index 9a63bc9e50d42ad13403f5ed81d6fc6aff1a20b5..20579ba1383df554527a5de5fb161dfd645145a3 100644 (file)
@@ -34,6 +34,7 @@ class EmailBatch extends Resource
         'subject',
     ];
 
+
     /**
      * Get the fields displayed by the resource.
      *
index bc8a67c3972b57b992a08d82e8df92d705095589..5c15551c2b7dbc28ce5a06d3028d8bdbd9f7d9a0 100644 (file)
@@ -3,6 +3,7 @@
 namespace App\Nova;
 
 use Illuminate\Http\Request;
+use Laravel\Nova\Fields\Boolean;
 use Laravel\Nova\Fields\HasMany;
 use Laravel\Nova\Fields\ID;
 use Laravel\Nova\Fields\Text;
@@ -33,6 +34,15 @@ class Organization extends Resource
         'name',
     ];
 
+
+    public static $group = "CRM";
+
+    public static function label()
+    {
+        return "Organisations";
+    }
+
+
     /**
      * Get the fields displayed by the resource.
      *
@@ -45,6 +55,7 @@ class Organization extends Resource
             ID::make()->sortable(),
             Text::make('Nom', 'name'),
             HasMany::make('Membres', 'members', User::class),
+            Boolean::make('Abonnement actif', 'subscription_active')
 
         ];
     }
index 2e1b004941d2a1af658894d37b9c969e2cdc96f6..e3301acbc49af8dd765bc81ca0457c1819222316 100644 (file)
@@ -32,6 +32,8 @@ class PdfFile extends Resource
         'title',
     ];
 
+
+
     /**
      * Get the fields displayed by the resource.
      *
index 074ee2c1ae65b3c736eecf471f82cce443b3f913..16c87ffed7f3879052fb7eaed08d44b803eaf949 100644 (file)
@@ -31,6 +31,8 @@ class SearchableText extends Resource
         'id',
     ];
 
+
+
     /**
      * Get the fields displayed by the resource.
      *
index 3167c3243c163208c928b0772206868314a9de62..17da52526dc540aac41a50dab53a438f03f32c93 100644 (file)
@@ -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.
      *
index 839749a4ebb2254812852c73dc224412e01761ce..ea86e6940f826909ea10a17421af7eb30b7b23d8 100644 (file)
@@ -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);
index 17ed76bc07b07e02b0f2f014f8e09100742bf066..1e96858b6bd53eaabe9f1332c81cb30f2c60929e 100644 (file)
@@ -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;
         }
 
index 9bba3492f9461cd13e59176a2eb782cc03432bea..d5439e2d61570a5eb699d66573cf3a8b0ea1368f 100644 (file)
@@ -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,
index a0083ccf175b9ee5601001b31da2a68ddf71ff46..e6d46b7dcaaf4a42874dd89f05e3d4c1090df914 100644 (file)
@@ -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
 
 
 
+
+
+
 }
index 0de210cf0fc408f7e56e57d85ef76de713c11567..e237267886cb74df1e3bb84b09e0ea05938e2955 100644 (file)
@@ -80,7 +80,7 @@ return [
     |
     */
 
-    'locale' => 'en',
+    'locale' => 'fr',
 
     /*
     |--------------------------------------------------------------------------
index d8a95c8481051867576fe9b64be9606c2fc0b228..fe97364bbfee63730fc46ba9bd5893c8f745bc5b 100644 (file)
@@ -78,7 +78,7 @@ return [
 
          'admins' => [
              'driver' => 'eloquent',
-             'model' => \A17\Twill\Models\User::class,
+             'model' => \App\Models\Admin::class,
          ],
     ],
 
index 025203047dc419ac3d036de3120036128c5ecdf1..16649dc3973529f86ad28cac15ff13ab377c5eff 100644 (file)
@@ -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 (file)
index 0000000..a8f4655
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateAccessLogsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('access_logs', function (Blueprint $table) {
+            $table->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 (file)
index 0000000..38d84eb
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateAccessTokensTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('login_tokens', function (Blueprint $table) {
+            $table->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 (file)
index 0000000..df5faa9
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class UserPlaceUpdateToUsers extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->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');
+        });
+    }
+}
index 559cf3944a04ca6b029619efc85e992b4a5a23bc..c92b75bcdf25e7d14daafad8b1e53a888b4bc34a 100644 (file)
@@ -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;
+}
+
index e01b68c83575a1d68546337f5a64ad2eb652c44f..35072fadf7bdfe6490bc1a578e9f8a6efe44697e 100644 (file)
@@ -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                    <p>Bonjour $nom$,</p>\n                    <p>Voici la nouvelle \xE9dition du jour !</p>\n                ",
+        content: "\n                    <p>Bonjour %recipient.name%,</p>\n                    <p>Voici la nouvelle \xE9dition du jour !</p>\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
index d6801527aa6f1cd3677ae5388e95a3e73db70a38..ebd4c27e1455768197a982dc127f31d001c854d9 100644 (file)
@@ -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);
 
 /**
index 7a599b944af754b4420cd56c28460916ae249cff..da49c140eafe6712e424397c8fda789911ba1b90 100644 (file)
@@ -1,12 +1,23 @@
 <template>
 
     <div class="card w-100">
-        <img class="card-img-top" :src="imgLink" alt="Cover">
+        <img class="card-img-top" :src="hit.file.cover" alt="Cover">
         <div class="card-body">
             <h5 class="card-title">{{hit.file.collection}} - <strong>{{hit.file.ref}}</strong></h5>
-            <p class="card-text"><ais-snippet attribute="content" :hit="hit"/></p>
+
+            <p class="card-text">
+                <span class="badge badge-light mr-1" v-for="tag in hit.file.tags">
+                    <i class="fas fa-tag"></i>&nbsp;{{tag}}
+                </span>
+            </p>
+            <p class="card-text">
+                <ais-snippet attribute="content" :hit="hit" v-if="hit._highlightResult.content.matchLevel !== 'none'"/>
+            </p>
         </div>
-        <a :href="viewLink" class="btn btn-primary"><i class="fas fa-book-open"></i>&nbsp;Lire cette édition</a>
+
+        <md-button :href="viewLink" class="md-raised " target="_blank"><i class="fas fa-book-open"></i>&nbsp;Lire cette édition</md-button>
+
+
 
 
 
             }
         },
         computed: {
-            imgLink: function() {
-                return `/files/${this.hit.file.slug}/cover`;
-            },
             viewLink: function () {
-                return `/view/${this.hit.file.slug}`;
+                return `/view/${this.hit.file.slug}#page=${this.hit.page}`;
             }
 
         }
index 54e3f262e5e5cbeab603cbb64316f4ce408962da..fac11f604a8d021cb7027e45d371dc5cbc41ce01 100644 (file)
 
 
         <div class="col-md-12">
-            <ais-search-box placeholder="Rechercher..." class="searchbox" />
-            <ais-stats></ais-stats>
+            <div class="px-3">
+                <ais-search-box placeholder="Rechercher..." class="searchbox " />
+
+            </div>
+            <ais-stats class="mt-2 ml-3"></ais-stats>
 
               <div class="my-4">
                 <ais-infinite-hits :class-names="{
index bd2737031efe84215d2a0d27cbc7e10d052788bb..63315efb53f0d008e930fcf76b1e576a0b06f231 100644 (file)
@@ -7,14 +7,14 @@
                 <div class="form-group">
                     <label for="file_collection">Collection *</label>
                     <select class="form-control" id="file_collection" v-model="file_collection">
-                        <option v-for="collection in collections" :value="collection.id">{{collection.name}}</option>
+                        <option v-for="collection in collections" :value="collection.id" v-text="collection.name"></option>
                     </select>
                 </div>
             </div>
             <div class="col-4">
                 <div class="form-group">
                     <label for="file_ref">Numéro *</label>
-                    <input type="email" class="form-control is-invalid" id="file_ref" placeholder="ex: 482" v-model="file_ref" required>
+                    <input type="email" class="form-control is-valid" id="file_ref" :placeholder="nextRef" v-model="file_ref" required>
                 </div>
             </div>
             <div class="col-12">
@@ -42,8 +42,8 @@
                 }"
                 :data="file_data"
                 ref="upload">
-                    <i class="fa fa-plus"></i>
-                    Sélectionnez un fichier
+                <i class="fa fa-plus"></i>
+                Sélectionnez un fichier
             </file-upload>
         </div>
 
             <div v-if="fileValid">
                 <p class="mb-3"><strong>Fichier sélectionné : </strong> {{files[0].name}} </p>
 
-<!--                <button type="button" class="btn btn-default btn-primary" v-if="!$refs.upload || !$refs.upload.active" @click.prevent="$refs.upload.active = true">-->
-<!--                    <i class="fa fa-arrow-up" aria-hidden="true"></i>-->
-<!--                    Envoyer-->
-<!--                </button>-->
-<!--                <button type="button" class="btn btn-default btn-danger"  v-else @click.prevent="$refs.upload.active = false">-->
-<!--                    <i class="fa fa-stop" aria-hidden="true"></i>-->
-<!--                    Arrêter-->
-<!--                </button>-->
+                <!--                <button type="button" class="btn btn-default btn-primary" v-if="!$refs.upload || !$refs.upload.active" @click.prevent="$refs.upload.active = true">-->
+                <!--                    <i class="fa fa-arrow-up" aria-hidden="true"></i>-->
+                <!--                    Envoyer-->
+                <!--                </button>-->
+                <!--                <button type="button" class="btn btn-default btn-danger"  v-else @click.prevent="$refs.upload.active = false">-->
+                <!--                    <i class="fa fa-stop" aria-hidden="true"></i>-->
+                <!--                    Arrêter-->
+                <!--                </button>-->
 
             </div>
 
             <div v-else class="alert alert-warning" role="alert">
-              <span class="font-bold">Fichier incorrect</span>
-              <span>Le fichier {{uploadedFile.name}} n'est pas valide, veuillez choisir un fichier .pdf !</span>
+                <span class="font-bold">Fichier incorrect</span>
+                <span>Le fichier {{uploadedFile.name}} n'est pas valide, veuillez choisir un fichier .pdf !</span>
             </div>
 
 
 </template>
 
 
-<!--@todo Check NEXT button activation-->
 <script>
     import FileUpload from 'vue-upload-component';
     import ProgressBar from 'vue-simple-progress';
             }
         },
         computed: {
-          uploadedFile: function(){
-              return (this.files.length > 0) ? this.files[0] : null;
-          },
-
-          fileValid: function() {
-              if(this.files.length < 1)
-                  return;
-
-              return this.checkFileValid(this.files[0]);
-          },
-          file_data: function() {
-              return {
-                  collection_id: this.file_collection,
-                  ref: this.file_ref,
-                  tags: this.tags
-              }
-          }
+            uploadedFile: function(){
+                return (this.files.length > 0) ? this.files[0] : null;
+            },
+            nextRef: function(){
+                return this.getNextRef(this.file_collection);
+            },
+
+
+            fileValid: function() {
+                if(this.files.length < 1)
+                    return;
+
+                return this.checkFileValid(this.files[0]);
+            },
+            file_data: function() {
+                return {
+                    collection_id: this.file_collection,
+                    ref: this.file_ref,
+                    tags: this.tags
+                }
+            }
         },
         watch: {
-          file_ref: function(value){
-              if(value.length > 0) {
-                  this.status = "ready";
-                  $('#file_ref').addClass('is-valid').removeClass('is-invalid')
-              } else {
-                  this.status = "start";
-                  $('#file_ref').addClass('is-invalid').removeClass('is-valid')
-
-              }
-
-          },
-          uploadedFile: function(file){
-              if (file.success){
-                  this.progressMode = 'indeterminate';
-                  Echo.private(`fileProcessingStatus.${file.response.data.slug}`)
-                      .listen('.status.update', this.processStatusUpdate)
-              }
-          }
+            file_ref: function(value){
+                if(value.length > 0) {
+                    this.status = "ready";
+                    $('#file_ref').addClass('is-valid').removeClass('is-invalid')
+                } else {
+                    this.status = "start";
+                    $('#file_ref').addClass('is-invalid').removeClass('is-valid')
+
+                }
+
+            },
+            uploadedFile: function(file){
+                if (file.success){
+                    this.progressMode = 'indeterminate';
+                    Echo.private(`fileProcessingStatus.${file.response.data.slug}`)
+                        .listen('.status.update', this.processStatusUpdate)
+                }
+            },
+
         },
         methods: {
             processStatusUpdate(e){
 
             },
 
+            getNextRef(collection_id) {
+                let collection = this.collections.find(c => c.id === collection_id);
+                if(collection === undefined)
+                    return null;
+                let lastRef = collection.files[0].ref;
+                return lastRef.replace(/(\d+)+/g, function(match, number) {
+                    return parseInt(number)+1;
+                });
+
+            },
+
             checkFileValid(file) {
                 return file.type === 'application/pdf';
             },
 
             inputFile(newFile, oldFile) {
-              if (
-                  newFile &&
-                  (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error)
-                  && !this.$refs.upload.active
-                  && this.checkFileValid(newFile)
-              ) {
-                  this.$refs.upload.active = true
-                  this.status = 'uploading';
-              }
-
-              if(newFile && newFile.success === true) {
-                  this.status = 'processing';
-              }
+                if (
+                    newFile &&
+                    (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error)
+                    && !this.$refs.upload.active
+                    && this.checkFileValid(newFile)
+                ) {
+                    this.$refs.upload.active = true;
+                    this.status = 'uploading';
+                }
+
+                if(newFile && newFile.success === true) {
+                    this.status = 'processing';
+                }
             }
         },
         mounted() {
 
-            axios.get('/publish/collections').then(d => this.collections = d.data.data);
+            axios.get('/publish/collections').then(d => {
+                this.collections = d.data.data;
+                this.file_ref  = this.getNextRef(1);
+            });
+
 
             axios.get('/publish/tags').then(d => {
                 let tags = d.data.data;
index 8b6e65599c604240d5cf79bfb08f33ee67c60d0d..6ef9603d75d79082b448ff3e90d9ec650c1024dc 100644 (file)
@@ -66,7 +66,7 @@
                 editor: ClassicEditor,
                 email: {
                     content: `
-                        <p>Bonjour $nom$,</p>
+                        <p>Bonjour %recipient.name%,</p>
                         <p>Voici la nouvelle édition du jour !</p>
                     `,
                     subject: `[${this.$root.publishState.file.title}] `
index 1ebcc0ceb2280469a9d30e8526574eab952a0cb1..e769a02433b08b11fb39907fc0fff1d30a10c613 100644 (file)
   animation:blink-fade 1000ms infinite;
   -webkit-animation:blink-fade 1000ms infinite;
 }
+
+.ais-SearchBox-input {
+    @extend .form-control;
+    width: calc(100% - 20px);
+    display: inline-block;
+}
+
+mark.ais-Snippet-highlighted, mark.mark {
+     padding: 0;
+    background-color: #fff252;
+}
index 8e5c6e52ea9d4c4fbd6f40f93e2377daad5facde..18919f389502f8b57b0f8cf69dd77920b9559441 100644 (file)
@@ -11,7 +11,7 @@
     {!! $content !!}
 
     <a
-        href="{!! $link ?? '' !!}"
+        href="{{ $link ?? '%recipient.file_url%'  }}"
         target="_blank"
         class="btn btn-success mx-auto my-3 d-block"
         style="width: fit-content;"
index 5a5c848449d50af918d57d66c28dea057afd7ec8..17d254b242c21bddb0482455425b8be005a7a30c 100644 (file)
@@ -61,8 +61,7 @@ Route::domain(env('CLIENT_DOMAIN_NAME'))->group(function() {
     Route::get('/setup', 'Auth\\RegisterController@setup')->name('setup');
 
     /** Flowpaper viewer */
-    Route::prefix('/view')->group(function () {
-        Route::get('/data/{file:slug}.bin', 'FlowpaperController@outputFile')->name('flowpaper.bin');
+    Route::prefix('/view')->middleware('login.token')->group(function () {
         Route::get('/{file:slug}', 'FlowpaperController@view')->name('flowpaper.view');
     });