Roberto Santini 1 hafta önce
ebeveyn
işleme
5802c483d3

+ 1
- 1
app/DataTables/ContrattoTemplateDataTable.php Dosyayı Görüntüle

@@ -37,7 +37,7 @@ class ContrattoTemplateDataTable extends DataTable
37 37
 
38 38
   public function query(ContrattoTemplate $model): QueryBuilder
39 39
   {
40
-    return $model->newQuery()->withCount(['contrattoTemplateTodos', 'contrattoTemplateRigas']);
40
+    return $model->newQuery()->withCount(['todos', 'righe']);
41 41
   }
42 42
 
43 43
   public function html(): HtmlBuilder

+ 3
- 3
app/DataTables/GruppoDataTable.php Dosyayı Görüntüle

@@ -72,11 +72,11 @@ class GruppoDataTable extends DataTable
72 72
       ->text('<i class="fas fa-plus"></i> Nuovo gruppo'));
73 73
     }
74 74
 
75
-    $aziende = Azienda::orderBy('nome', 'asc')->get()->pluck('id', 'nome');
75
+    //$aziende = Azienda::orderBy('nome', 'asc')->get()->pluck('id', 'nome');
76 76
 
77 77
     $fields = [
78 78
       Fields\Text::make('label_gruppo')->label('Nome gruppo'),
79
-      Fields\Select::make('azienda_id')->label('Azienda')->options($aziende)->placeholder('Nessuna'),
79
+      //Fields\Select::make('azienda_id')->label('Azienda')->options($aziende)->placeholder('Nessuna'),
80 80
       Fields\Checkbox::make('componenti_gruppo', 'Utenti')->options(User::soloUtenti()->orderBy('nome', 'asc')->get()->pluck('id', 'full_name') )->separator(","),
81 81
       Fields\Hidden::make('is_gruppo')->default(1),
82 82
     ];
@@ -107,7 +107,7 @@ class GruppoDataTable extends DataTable
107 107
     {
108 108
       $columns = [
109 109
         Column::make('label_gruppo')->title('Nome gruppo'),
110
-        Column::make('azienda_id')->title('Azienda')->data('azienda_display')->name('azienda_id')->searchable(false),
110
+        //Column::make('azienda_id')->title('Azienda')->data('azienda_display')->name('azienda_id')->searchable(false),
111 111
         Column::make('componenti_gruppo_label')->title('Utenti'),
112 112
         Column::computed('action')
113 113
         ->exportable(false)

+ 27
- 0
app/DataTables/UserDataTable.php Dosyayı Görüntüle

@@ -27,6 +27,23 @@ class UserDataTable extends DataTable
27 27
   */
28 28
   public function dataTable(QueryBuilder $query): EloquentDataTable
29 29
   {
30
+    $gruppiByUser = [];
31
+    User::query()
32
+      ->where('is_gruppo', true)
33
+      ->get(['id', 'componenti_gruppo'])
34
+      ->each(function (User $gruppo) use (&$gruppiByUser) {
35
+        $members = $gruppo->componenti_gruppo;
36
+        if (! is_array($members)) {
37
+          $members = $members ? explode(',', (string) $members) : [];
38
+        }
39
+        foreach ($members as $userId) {
40
+          $userId = (int) $userId;
41
+          if ($userId) {
42
+            $gruppiByUser[$userId][] = $gruppo->id;
43
+          }
44
+        }
45
+      });
46
+
30 47
     return (new EloquentDataTable($query))
31 48
     ->addColumn('action', function($entity){
32 49
       return view('user.menu', ['entity' => $entity]);
@@ -45,6 +62,9 @@ class UserDataTable extends DataTable
45 62
         ? '<span class="badge bg-label-success">Sì</span>'
46 63
         : '<span class="badge bg-label-secondary">No</span>';
47 64
     })
65
+    ->addColumn('gruppi_appartenenza', function($entity) use ($gruppiByUser) {
66
+      return implode(',', $gruppiByUser[$entity->id] ?? []);
67
+    })
48 68
     ->editColumn('azienda_id', function ($entity) {
49 69
       return $entity->azienda_id ? (string) $entity->azienda_id : '';
50 70
     })
@@ -88,6 +108,11 @@ class UserDataTable extends DataTable
88 108
     }
89 109
 
90 110
     $aziende = Azienda::orderBy('nome', 'asc')->get()->pluck('id', 'nome');
111
+    $gruppi = User::query()
112
+      ->where('is_gruppo', true)
113
+      ->orderBy('label_gruppo', 'asc')
114
+      ->get()
115
+      ->pluck('id', 'full_name');
91 116
 
92 117
     $fields = [
93 118
       Fields\Text::make('nome')->label('Nome'),
@@ -97,6 +122,7 @@ class UserDataTable extends DataTable
97 122
       Fields\Select::make('ruolo_id')->label('Ruolo')->options($roles)->default(Role::where('name', 'user')->first()->id),
98 123
       Fields\Select::make('azienda_id')->label('Azienda')->options($aziende)->placeholder('Nessuna'),
99 124
       Fields\Radio::make('responsabile_azienda')->label('Responsabile azienda')->options([['label' => 'Si', 'value' => 1], ['label' => 'No', 'value' => 0]])->default(0),
125
+      Fields\Checkbox::make('gruppi_appartenenza', 'Gruppi')->options($gruppi)->separator(','),
100 126
       Fields\Password::make('password_new')->label('Password (lasciare vuoto per non modificare)'),
101 127
       Fields\Password::make('password_new_confirmation')->label('Conferma password'),
102 128
     ];
@@ -133,6 +159,7 @@ class UserDataTable extends DataTable
133 159
         Column::make('ruolo_id')->title('Ruolo')->searchable(false)->name('ruolo_id')->data('ruolo_display'),
134 160
         Column::make('azienda_id')->title('Azienda')->data('azienda_display')->name('azienda_id')->searchable(false),
135 161
         Column::make('responsabile_azienda')->title('Resp. azienda')->data('responsabile_azienda_label')->name('responsabile_azienda')->searchable(false),
162
+        Column::make('gruppi_appartenenza')->visible(false)->searchable(false)->orderable(false),
136 163
         Column::computed('action')
137 164
         ->exportable(false)
138 165
         ->printable(false)

+ 67
- 2
app/DataTables/UserDataTableEditor.php Dosyayı Görüntüle

@@ -15,8 +15,9 @@ use Illuminate\Support\Str;
15 15
 class UserDataTableEditor extends DataTablesEditor
16 16
 {
17 17
   protected $model = User::class;
18
-  // protected $uploadDir = '';
19
-  // protected $disk = 'modelliRicevute';
18
+
19
+  /** @var mixed false = non inviato */
20
+  protected $pendingGruppiAppartenenza = false;
20 21
 
21 22
   protected $messages = [
22 23
     'nome.required' => 'Il nome è richiesto',
@@ -94,6 +95,11 @@ class UserDataTableEditor extends DataTablesEditor
94 95
     {
95 96
         unset($data['password_new_confirmation']);
96 97
 
98
+        if (array_key_exists('gruppi_appartenenza', $data)) {
99
+            $this->pendingGruppiAppartenenza = $data['gruppi_appartenenza'];
100
+            unset($data['gruppi_appartenenza']);
101
+        }
102
+
97 103
         if (array_key_exists('azienda_id', $data) && ($data['azienda_id'] === '' || $data['azienda_id'] === null)) {
98 104
             $data['azienda_id'] = null;
99 105
         }
@@ -121,6 +127,7 @@ class UserDataTableEditor extends DataTablesEditor
121 127
   {
122 128
 
123 129
     $model->roles()->sync([$data['ruolo_id']]);
130
+    $this->applyPendingGruppiAppartenenza($model);
124 131
 
125 132
     return parent::created($model, $data);
126 133
   }
@@ -131,6 +138,64 @@ class UserDataTableEditor extends DataTablesEditor
131 138
     $model->roles()->sync([$data['ruolo_id']]);
132 139
     return $data;
133 140
   }
141
+
142
+  public function updated(Model $model, array $data): Model
143
+  {
144
+    $this->applyPendingGruppiAppartenenza($model);
145
+
146
+    return parent::updated($model, $data);
147
+  }
148
+
149
+  protected function applyPendingGruppiAppartenenza(Model $user): void
150
+  {
151
+    if ($this->pendingGruppiAppartenenza === false) {
152
+      return;
153
+    }
154
+
155
+    $desired = $this->parseIdList($this->pendingGruppiAppartenenza);
156
+    $this->pendingGruppiAppartenenza = false;
157
+
158
+    $gruppi = User::query()->where('is_gruppo', true)->get();
159
+    $userId = (int) $user->id;
160
+
161
+    foreach ($gruppi as $gruppo) {
162
+      $members = $gruppo->componenti_gruppo;
163
+      if (! is_array($members)) {
164
+        $members = $members ? explode(',', (string) $members) : [];
165
+      }
166
+      $members = array_values(array_unique(array_filter(array_map('intval', $members))));
167
+
168
+      $shouldBelong = in_array((int) $gruppo->id, $desired, true);
169
+      $belongs = in_array($userId, $members, true);
170
+
171
+      if ($shouldBelong && ! $belongs) {
172
+        $members[] = $userId;
173
+        $gruppo->componenti_gruppo = $members;
174
+        $gruppo->save();
175
+      } elseif (! $shouldBelong && $belongs) {
176
+        $members = array_values(array_filter($members, fn ($id) => (int) $id !== $userId));
177
+        $gruppo->componenti_gruppo = $members ?: null;
178
+        $gruppo->save();
179
+      }
180
+    }
181
+  }
182
+
183
+  /**
184
+   * @return array<int>
185
+   */
186
+  protected function parseIdList(mixed $value): array
187
+  {
188
+    if (is_array($value)) {
189
+      $ids = $value;
190
+    } elseif ($value === null || $value === '') {
191
+      $ids = [];
192
+    } else {
193
+      $ids = explode(',', (string) $value);
194
+    }
195
+
196
+    return array_values(array_unique(array_filter(array_map('intval', $ids))));
197
+  }
198
+
134 199
   public function messages(): array
135 200
   {
136 201
     return $this->messages;

+ 51
- 0
app/Http/Controllers/UserController.php Dosyayı Görüntüle

@@ -10,6 +10,9 @@ use App\Models\User;
10 10
 use App\DataTables\UserDataTableEditor;
11 11
 use App\DataTables\UserDataTable;
12 12
 use Illuminate\Support\Facades\Auth;
13
+use Illuminate\Support\Facades\Cookie;
14
+use BadMethodCallException;
15
+use Illuminate\Auth\SessionGuard;
13 16
 use Notification;
14 17
 use Session;
15 18
 use PDF;
@@ -113,6 +116,54 @@ class UserController extends Controller implements HasMiddleware
113 116
     return redirect()->route('admin.user.scheda', $user->id)->with('success', 'Scheda aggiornata.');
114 117
   }
115 118
 
119
+  public function admin_impersonate(Request $request, $user_id)
120
+  {
121
+    abort_unless(Auth::user()->hasRole('superadmin'), 403);
122
+
123
+    $user = User::findOrFail($user_id);
124
+    $user->refresh();
125
+
126
+    $impersonatorId = Auth::id();
127
+
128
+    /** @var SessionGuard $guard */
129
+    $guard = Auth::guard('web');
130
+    $guard->login($user);
131
+    Auth::shouldUse('web');
132
+
133
+    $request->session()->put([
134
+      'impersonating' => true,
135
+      'impersonator_id' => $impersonatorId,
136
+      'impersonated_id' => $user->id,
137
+    ]);
138
+    $request->session()->forget('login.id');
139
+
140
+    $plain = $user->getAuthPassword();
141
+    $stored = $plain;
142
+    if ($plain) {
143
+      try {
144
+        $stored = $guard->hashPasswordForCookie($plain);
145
+      } catch (BadMethodCallException) {
146
+      }
147
+    }
148
+    $guardNames = array_unique(array_merge(
149
+      ['web', 'sanctum'],
150
+      (array) config('sanctum.guard', ['web']),
151
+      array_keys(config('auth.guards', []))
152
+    ));
153
+    foreach ($guardNames as $guardName) {
154
+      $key = 'password_hash_'.$guardName;
155
+      if ($plain) {
156
+        $request->session()->put($key, $stored);
157
+      } else {
158
+        $request->session()->forget($key);
159
+      }
160
+    }
161
+
162
+    $request->session()->save();
163
+
164
+    return redirect()->route('dashboard')->withCookie(Cookie::forget($guard->getRecallerName()));
165
+  }
166
+
116 167
   public function admin_store(UserDataTableEditor $editor)
117 168
   {
118 169
     $request = request();

+ 1
- 0
composer.json Dosyayı Görüntüle

@@ -13,6 +13,7 @@
13 13
     "barryvdh/laravel-dompdf": "^3.1",
14 14
     "elibyy/tcpdf-laravel": "^11.5",
15 15
     "imtigger/laravel-job-status": "^1.2",
16
+    "jdavidbakr/mail-tracker": "^8.3",
16 17
     "laravel/framework": "^12.0",
17 18
     "laravel/horizon": "^5.43",
18 19
     "laravel/jetstream": "*",

+ 413
- 1
composer.lock Dosyayı Görüntüle

@@ -4,8 +4,218 @@
4 4
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 5
         "This file is @generated automatically"
6 6
     ],
7
-    "content-hash": "3532f68b204cd86982b0b4fd6c4252a7",
7
+    "content-hash": "36ecee00ae097944ec3042aa231e6116",
8 8
     "packages": [
9
+        {
10
+            "name": "aws/aws-crt-php",
11
+            "version": "v1.2.7",
12
+            "source": {
13
+                "type": "git",
14
+                "url": "https://github.com/awslabs/aws-crt-php.git",
15
+                "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e"
16
+            },
17
+            "dist": {
18
+                "type": "zip",
19
+                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e",
20
+                "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e",
21
+                "shasum": ""
22
+            },
23
+            "require": {
24
+                "php": ">=5.5"
25
+            },
26
+            "require-dev": {
27
+                "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5",
28
+                "yoast/phpunit-polyfills": "^1.0"
29
+            },
30
+            "suggest": {
31
+                "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality."
32
+            },
33
+            "type": "library",
34
+            "autoload": {
35
+                "classmap": [
36
+                    "src/"
37
+                ]
38
+            },
39
+            "notification-url": "https://packagist.org/downloads/",
40
+            "license": [
41
+                "Apache-2.0"
42
+            ],
43
+            "authors": [
44
+                {
45
+                    "name": "AWS SDK Common Runtime Team",
46
+                    "email": "aws-sdk-common-runtime@amazon.com"
47
+                }
48
+            ],
49
+            "description": "AWS Common Runtime for PHP",
50
+            "homepage": "https://github.com/awslabs/aws-crt-php",
51
+            "keywords": [
52
+                "amazon",
53
+                "aws",
54
+                "crt",
55
+                "sdk"
56
+            ],
57
+            "support": {
58
+                "issues": "https://github.com/awslabs/aws-crt-php/issues",
59
+                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7"
60
+            },
61
+            "time": "2024-10-18T22:15:13+00:00"
62
+        },
63
+        {
64
+            "name": "aws/aws-php-sns-message-validator",
65
+            "version": "1.10.2",
66
+            "source": {
67
+                "type": "git",
68
+                "url": "https://github.com/aws/aws-php-sns-message-validator.git",
69
+                "reference": "dc6abc2dfe30115f39c5196c7543b9509f511bb8"
70
+            },
71
+            "dist": {
72
+                "type": "zip",
73
+                "url": "https://api.github.com/repos/aws/aws-php-sns-message-validator/zipball/dc6abc2dfe30115f39c5196c7543b9509f511bb8",
74
+                "reference": "dc6abc2dfe30115f39c5196c7543b9509f511bb8",
75
+                "shasum": ""
76
+            },
77
+            "require": {
78
+                "ext-openssl": "*",
79
+                "php": ">=8.1",
80
+                "psr/http-message": "^2.0"
81
+            },
82
+            "require-dev": {
83
+                "guzzlehttp/psr7": "^2.4.5",
84
+                "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5",
85
+                "squizlabs/php_codesniffer": "^2.8.1 || ^3.13.6",
86
+                "yoast/phpunit-polyfills": "^1.0"
87
+            },
88
+            "type": "library",
89
+            "autoload": {
90
+                "psr-4": {
91
+                    "Aws\\Sns\\": "src/"
92
+                }
93
+            },
94
+            "notification-url": "https://packagist.org/downloads/",
95
+            "license": [
96
+                "Apache-2.0"
97
+            ],
98
+            "authors": [
99
+                {
100
+                    "name": "Amazon Web Services",
101
+                    "homepage": "http://aws.amazon.com"
102
+                }
103
+            ],
104
+            "description": "Amazon SNS message validation for PHP",
105
+            "homepage": "http://aws.amazon.com/sdkforphp",
106
+            "keywords": [
107
+                "SNS",
108
+                "amazon",
109
+                "aws",
110
+                "cloud",
111
+                "message",
112
+                "sdk",
113
+                "webhooks"
114
+            ],
115
+            "support": {
116
+                "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
117
+                "issues": "https://github.com/aws/aws-sns-message-validator/issues",
118
+                "source": "https://github.com/aws/aws-php-sns-message-validator/tree/1.10.2"
119
+            },
120
+            "time": "2026-09-03T15:26:29+00:00"
121
+        },
122
+        {
123
+            "name": "aws/aws-sdk-php",
124
+            "version": "3.394.9",
125
+            "source": {
126
+                "type": "git",
127
+                "url": "https://github.com/aws/aws-sdk-php.git",
128
+                "reference": "c5e7a247fa6aeac7355d5be88f38873480789c71"
129
+            },
130
+            "dist": {
131
+                "type": "zip",
132
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c5e7a247fa6aeac7355d5be88f38873480789c71",
133
+                "reference": "c5e7a247fa6aeac7355d5be88f38873480789c71",
134
+                "shasum": ""
135
+            },
136
+            "require": {
137
+                "aws/aws-crt-php": "^1.2.3",
138
+                "ext-json": "*",
139
+                "ext-pcre": "*",
140
+                "ext-simplexml": "*",
141
+                "guzzlehttp/guzzle": "^7.8.2 || ^8.0",
142
+                "guzzlehttp/promises": "^2.0.3 || ^3.0",
143
+                "guzzlehttp/psr7": "^2.6.3 || ^3.0",
144
+                "mtdowling/jmespath.php": "^2.9.1",
145
+                "php": ">=8.1",
146
+                "psr/http-message": "^1.0 || ^2.0",
147
+                "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0"
148
+            },
149
+            "require-dev": {
150
+                "andrewsville/php-token-reflection": "^1.4",
151
+                "aws/aws-php-sns-message-validator": "~1.0",
152
+                "behat/behat": "~3.0",
153
+                "composer/composer": "^2.7.8",
154
+                "dms/phpunit-arraysubset-asserts": "^v0.5.0",
155
+                "doctrine/cache": "~1.4",
156
+                "ext-dom": "*",
157
+                "ext-openssl": "*",
158
+                "ext-sockets": "*",
159
+                "phpunit/phpunit": "^10.0",
160
+                "psr/cache": "^2.0 || ^3.0",
161
+                "psr/simple-cache": "^2.0 || ^3.0",
162
+                "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
163
+                "yoast/phpunit-polyfills": "^2.0"
164
+            },
165
+            "suggest": {
166
+                "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
167
+                "doctrine/cache": "To use the DoctrineCacheAdapter",
168
+                "ext-curl": "To send requests using cURL",
169
+                "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
170
+                "ext-pcntl": "To use client-side monitoring",
171
+                "ext-sockets": "To use client-side monitoring"
172
+            },
173
+            "type": "library",
174
+            "extra": {
175
+                "branch-alias": {
176
+                    "dev-master": "3.0-dev"
177
+                }
178
+            },
179
+            "autoload": {
180
+                "files": [
181
+                    "src/functions.php"
182
+                ],
183
+                "psr-4": {
184
+                    "Aws\\": "src/"
185
+                },
186
+                "exclude-from-classmap": [
187
+                    "src/data/"
188
+                ]
189
+            },
190
+            "notification-url": "https://packagist.org/downloads/",
191
+            "license": [
192
+                "Apache-2.0"
193
+            ],
194
+            "authors": [
195
+                {
196
+                    "name": "Amazon Web Services",
197
+                    "homepage": "https://aws.amazon.com"
198
+                }
199
+            ],
200
+            "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
201
+            "homepage": "https://aws.amazon.com/sdk-for-php",
202
+            "keywords": [
203
+                "amazon",
204
+                "aws",
205
+                "cloud",
206
+                "dynamodb",
207
+                "ec2",
208
+                "glacier",
209
+                "s3",
210
+                "sdk"
211
+            ],
212
+            "support": {
213
+                "forum": "https://github.com/aws/aws-sdk-php/discussions",
214
+                "issues": "https://github.com/aws/aws-sdk-php/issues",
215
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.394.9"
216
+            },
217
+            "time": "2026-09-04T18:07:00+00:00"
218
+        },
9 219
         {
10 220
             "name": "bacon/bacon-qr-code",
11 221
             "version": "v3.0.3",
@@ -1667,6 +1877,72 @@
1667 1877
             },
1668 1878
             "time": "2020-09-19T16:43:44+00:00"
1669 1879
         },
1880
+        {
1881
+            "name": "jdavidbakr/mail-tracker",
1882
+            "version": "8.3",
1883
+            "source": {
1884
+                "type": "git",
1885
+                "url": "https://github.com/jdavidbakr/mail-tracker.git",
1886
+                "reference": "6e72a16d190075117a4be94b5b1f895e3dccb1e0"
1887
+            },
1888
+            "dist": {
1889
+                "type": "zip",
1890
+                "url": "https://api.github.com/repos/jdavidbakr/mail-tracker/zipball/6e72a16d190075117a4be94b5b1f895e3dccb1e0",
1891
+                "reference": "6e72a16d190075117a4be94b5b1f895e3dccb1e0",
1892
+                "shasum": ""
1893
+            },
1894
+            "require": {
1895
+                "aws/aws-php-sns-message-validator": "^1.8",
1896
+                "aws/aws-sdk-php": "^3.372",
1897
+                "guzzlehttp/guzzle": "^7.2",
1898
+                "illuminate/support": "^10.0|^11.0|^12.0|^13.0",
1899
+                "php": "^8.2"
1900
+            },
1901
+            "require-dev": {
1902
+                "mockery/mockery": "^1.4.4",
1903
+                "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0",
1904
+                "phpunit/phpunit": "^9.5.10|^10.5|^11.5.3|^12.5.12"
1905
+            },
1906
+            "suggest": {
1907
+                "fedeisas/laravel-mail-css-inliner": "Automatically inlines CSS into all outgoing mail."
1908
+            },
1909
+            "type": "library",
1910
+            "extra": {
1911
+                "laravel": {
1912
+                    "providers": [
1913
+                        "jdavidbakr\\MailTracker\\MailTrackerServiceProvider"
1914
+                    ]
1915
+                }
1916
+            },
1917
+            "autoload": {
1918
+                "psr-4": {
1919
+                    "jdavidbakr\\MailTracker\\": "src"
1920
+                }
1921
+            },
1922
+            "notification-url": "https://packagist.org/downloads/",
1923
+            "license": [
1924
+                "MIT"
1925
+            ],
1926
+            "authors": [
1927
+                {
1928
+                    "name": "J David Baker",
1929
+                    "email": "me@jdavidbaker.com",
1930
+                    "homepage": "http://www.jdavidbaker.com",
1931
+                    "role": "Developer"
1932
+                }
1933
+            ],
1934
+            "description": "Logs and tracks all outgoing emails from Laravel",
1935
+            "homepage": "https://github.com/jdavidbakr/MailTracker",
1936
+            "keywords": [
1937
+                "MailTracker",
1938
+                "jdavidbakr"
1939
+            ],
1940
+            "support": {
1941
+                "issues": "https://github.com/jdavidbakr/mail-tracker/issues",
1942
+                "source": "https://github.com/jdavidbakr/mail-tracker/tree/8.3"
1943
+            },
1944
+            "time": "2026-06-24T21:40:06+00:00"
1945
+        },
1670 1946
         {
1671 1947
             "name": "laravel/fortify",
1672 1948
             "version": "v1.33.0",
@@ -3643,6 +3919,72 @@
3643 3919
             },
3644 3920
             "time": "2023-05-03T06:19:36+00:00"
3645 3921
         },
3922
+        {
3923
+            "name": "mtdowling/jmespath.php",
3924
+            "version": "2.9.2",
3925
+            "source": {
3926
+                "type": "git",
3927
+                "url": "https://github.com/jmespath/jmespath.php.git",
3928
+                "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8"
3929
+            },
3930
+            "dist": {
3931
+                "type": "zip",
3932
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/2157c5e50e813ec6a96c1eed3be7f64a20fb32a8",
3933
+                "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8",
3934
+                "shasum": ""
3935
+            },
3936
+            "require": {
3937
+                "php": "^7.2.5 || ^8.0",
3938
+                "symfony/polyfill-mbstring": "^1.17"
3939
+            },
3940
+            "require-dev": {
3941
+                "composer/xdebug-handler": "^3.0.3",
3942
+                "phpunit/phpunit": "^8.5.52"
3943
+            },
3944
+            "bin": [
3945
+                "bin/jp.php"
3946
+            ],
3947
+            "type": "library",
3948
+            "extra": {
3949
+                "branch-alias": {
3950
+                    "dev-master": "2.9-dev"
3951
+                }
3952
+            },
3953
+            "autoload": {
3954
+                "files": [
3955
+                    "src/JmesPath.php"
3956
+                ],
3957
+                "psr-4": {
3958
+                    "JmesPath\\": "src/"
3959
+                }
3960
+            },
3961
+            "notification-url": "https://packagist.org/downloads/",
3962
+            "license": [
3963
+                "MIT"
3964
+            ],
3965
+            "authors": [
3966
+                {
3967
+                    "name": "Graham Campbell",
3968
+                    "email": "hello@gjcampbell.co.uk",
3969
+                    "homepage": "https://github.com/GrahamCampbell"
3970
+                },
3971
+                {
3972
+                    "name": "Michael Dowling",
3973
+                    "email": "mtdowling@gmail.com",
3974
+                    "homepage": "https://github.com/mtdowling"
3975
+                }
3976
+            ],
3977
+            "description": "Declaratively specify how to extract elements from a JSON document",
3978
+            "keywords": [
3979
+                "json",
3980
+                "jsonpath"
3981
+            ],
3982
+            "support": {
3983
+                "issues": "https://github.com/jmespath/jmespath.php/issues",
3984
+                "source": "https://github.com/jmespath/jmespath.php/tree/2.9.2"
3985
+            },
3986
+            "time": "2026-07-06T18:56:19+00:00"
3987
+        },
3646 3988
         {
3647 3989
             "name": "myclabs/deep-copy",
3648 3990
             "version": "1.13.4",
@@ -6309,6 +6651,76 @@
6309 6651
             ],
6310 6652
             "time": "2024-09-25T14:21:43+00:00"
6311 6653
         },
6654
+        {
6655
+            "name": "symfony/filesystem",
6656
+            "version": "v7.4.18",
6657
+            "source": {
6658
+                "type": "git",
6659
+                "url": "https://github.com/symfony/filesystem.git",
6660
+                "reference": "90d412aa5277c6819db39e7605aa46b1019e3232"
6661
+            },
6662
+            "dist": {
6663
+                "type": "zip",
6664
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/90d412aa5277c6819db39e7605aa46b1019e3232",
6665
+                "reference": "90d412aa5277c6819db39e7605aa46b1019e3232",
6666
+                "shasum": ""
6667
+            },
6668
+            "require": {
6669
+                "php": ">=8.2",
6670
+                "symfony/polyfill-ctype": "~1.8",
6671
+                "symfony/polyfill-mbstring": "~1.8"
6672
+            },
6673
+            "require-dev": {
6674
+                "symfony/process": "^6.4|^7.0|^8.0"
6675
+            },
6676
+            "type": "library",
6677
+            "autoload": {
6678
+                "psr-4": {
6679
+                    "Symfony\\Component\\Filesystem\\": ""
6680
+                },
6681
+                "exclude-from-classmap": [
6682
+                    "/Tests/"
6683
+                ]
6684
+            },
6685
+            "notification-url": "https://packagist.org/downloads/",
6686
+            "license": [
6687
+                "MIT"
6688
+            ],
6689
+            "authors": [
6690
+                {
6691
+                    "name": "Fabien Potencier",
6692
+                    "email": "fabien@symfony.com"
6693
+                },
6694
+                {
6695
+                    "name": "Symfony Community",
6696
+                    "homepage": "https://symfony.com/contributors"
6697
+                }
6698
+            ],
6699
+            "description": "Provides basic utilities for the filesystem",
6700
+            "homepage": "https://symfony.com",
6701
+            "support": {
6702
+                "source": "https://github.com/symfony/filesystem/tree/v7.4.18"
6703
+            },
6704
+            "funding": [
6705
+                {
6706
+                    "url": "https://symfony.com/sponsor",
6707
+                    "type": "custom"
6708
+                },
6709
+                {
6710
+                    "url": "https://github.com/fabpot",
6711
+                    "type": "github"
6712
+                },
6713
+                {
6714
+                    "url": "https://github.com/nicolas-grekas",
6715
+                    "type": "github"
6716
+                },
6717
+                {
6718
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
6719
+                    "type": "tidelift"
6720
+                }
6721
+            ],
6722
+            "time": "2026-08-23T10:03:40+00:00"
6723
+        },
6312 6724
         {
6313 6725
             "name": "symfony/finder",
6314 6726
             "version": "v7.4.3",

+ 114
- 0
config/mail-tracker.php Dosyayı Görüntüle

@@ -0,0 +1,114 @@
1
+<?php
2
+
3
+return [
4
+    /**
5
+     * To disable the pixel injection, set this to false.
6
+     */
7
+    'inject-pixel'              => true,
8
+
9
+    /**
10
+     * To disable injecting tracking links, set this to false.
11
+     */
12
+    'track-links'               => true,
13
+
14
+    /**
15
+     * Optionally expire old emails, set to 0 to keep forever.
16
+     */
17
+    'expire-days'               => 60,
18
+
19
+    /**
20
+     * Where should the pingback URL route be?
21
+     */
22
+    'route'                     => [
23
+        'prefix'     => 'email',
24
+        'middleware' => ['api'],
25
+    ],
26
+
27
+    /**
28
+     * If we get a link click without a URL, where should we send it to?
29
+     */
30
+    'redirect-missing-links-to' => '/',
31
+
32
+    /**
33
+     * Where should the admin route be?
34
+     */
35
+    'admin-route'               => [
36
+        'enabled'    => true, // Should the admin routes be enabled?
37
+        'prefix'     => 'admin/email-manager',
38
+        'middleware' => [
39
+            'web',
40
+            'can:view-admin_dashboard',
41
+        ],
42
+    ],
43
+
44
+    /**
45
+     * Admin Template
46
+     * example
47
+     * 'name' => 'layouts.app' for Default emailTraking use 'emailTrakingViews::layouts.app'
48
+     * 'section' => 'content' for Default emailTraking use 'content'
49
+     * 'styles_section' => 'styles' for Default emailTraking use 'styles'
50
+     */
51
+    'admin-template'            => [
52
+        'name'    => 'layouts/layoutMaster',
53
+        'section' => 'content',
54
+    ],
55
+
56
+    /**
57
+     * Number of emails per page in the admin view
58
+     */
59
+    'emails-per-page'           => 30,
60
+
61
+    /**
62
+     * Date Format
63
+     */
64
+    'date-format'               => 'd/m/Y H:i',
65
+
66
+    /**
67
+     * Default database connection name (optional - use null for default)
68
+     */
69
+    'connection'                => null,
70
+
71
+    /**
72
+     * The SNS notification topic - if set, discard all notifications not in this topic.
73
+     */
74
+    'sns-topic'                 => null,
75
+
76
+    /**
77
+     * Determines whether the body of the email is logged in the sent_emails table
78
+     */
79
+    'log-content'               => true,
80
+
81
+    /**
82
+     * Determines whether the body should be stored in a file instead of database
83
+     * Can be either 'database' or 'filesystem'
84
+     */
85
+    'log-content-strategy'      => 'database',
86
+
87
+    /**
88
+     * What filesystem we use for storing content html files
89
+     */
90
+    'tracker-filesystem'        => null,
91
+    'tracker-filesystem-folder' => 'mail-tracker',
92
+
93
+    /**
94
+     * What queue should we dispatch our tracking jobs to?  Null will use the default queue.
95
+     */
96
+    'tracker-queue'             => null,
97
+
98
+    /**
99
+     * Size limit for content length stored in database
100
+     */
101
+    'content-max-size'          => 65535,
102
+
103
+    /**
104
+     * Length of time to default past email search - if set, will set the default past limit to the amount of days below (Ex: => 356)
105
+     */
106
+    'search-date-start'         => null,
107
+
108
+    /**
109
+     * Fallback method for when ValidateSignature has been introduced, but old links still need to be supported
110
+     */
111
+    'fallback-event-listeners' => [
112
+        \jdavidbakr\MailTracker\Listener\DomainExistsInContentListener::class,
113
+    ]
114
+];

+ 7
- 0
resources/menu/verticalMenu.json Dosyayı Görüntüle

@@ -145,6 +145,13 @@
145 145
       "url": "admin/casella-imap",
146 146
       "can": "view-casella-imap"
147 147
     },
148
+    {
149
+      "name": "Mail Tracker",
150
+      "icon": "menu-icon icon-base bx bx-mail-send",
151
+      "slug": "mailTracker_Index",
152
+      "url": "admin/email-manager",
153
+      "can": "view-admin_dashboard"
154
+    },
148 155
     {
149 156
       "name": "Utenti",
150 157
       "icon": "menu-icon icon-base bx bx-user",

+ 6
- 0
resources/views/gruppo/index.blade.php Dosyayı Görüntüle

@@ -41,6 +41,12 @@
41 41
       });
42 42
     });
43 43
 
44
+    window.LaravelDataTables["dataTable_gruppo-editor"].on('submitSuccess', function () {
45
+      if (window.LaravelDataTables && window.LaravelDataTables["dataTable_user"]) {
46
+        window.LaravelDataTables["dataTable_user"].ajax.reload(null, false);
47
+      }
48
+    });
49
+
44 50
   });
45 51
 </script>
46 52
 

+ 6
- 2
resources/views/user/index.blade.php Dosyayı Görüntüle

@@ -42,7 +42,7 @@ use Illuminate\Support\Facades\Auth;
42 42
 @section('content')
43 43
 
44 44
 <div class="row">
45
-  <div class="col-xxl-6 mb-4 mt-2">
45
+  <div class="col-xxl-8 mb-4 mt-2">
46 46
 
47 47
     @if(session('success'))
48 48
     <div class="alert alert-success alert-dismissible fade show" role="alert">
@@ -68,7 +68,7 @@ use Illuminate\Support\Facades\Auth;
68 68
     </div>
69 69
   </div>
70 70
 
71
-  <div class="col-xxl-6 mb-4 mt-2" id="div_gruppi">
71
+  <div class="col-xxl-4 mb-4 mt-2" id="div_gruppi">
72 72
   </div>
73 73
 </div>
74 74
 
@@ -120,6 +120,10 @@ use Illuminate\Support\Facades\Auth;
120 120
       });
121 121
     });
122 122
 
123
+    window.LaravelDataTables["dataTable_user-editor"].on('submitSuccess', function () {
124
+      loadGruppi();
125
+    });
126
+
123 127
   });
124 128
 </script>
125 129
 

+ 11
- 0
resources/views/user/menu.blade.php Dosyayı Görüntüle

@@ -22,6 +22,17 @@ use Illuminate\Support\Facades\Auth;
22 22
     </li>
23 23
     @endif
24 24
 
25
+    @if(Auth::user()->hasRole('superadmin'))
26
+    <form method="POST" action="{{ route('admin.user.impersonate', $entity->id) }}">
27
+    @csrf
28
+    <li>
29
+      <button type="submit" class="dropdown-item">
30
+        <i class="bx bx-user-check me-1"></i> Impersona
31
+      </button>
32
+    </li>
33
+    </form>
34
+    @endif
35
+
25 36
     @if(Auth::user()->can('remove-user'))
26 37
     <div class="dropdown-divider"></div>
27 38
     <li>

+ 30
- 0
resources/views/vendor/emailTrakingViews/emails/mensaje.blade.php Dosyayı Görüntüle

@@ -0,0 +1,30 @@
1
+@extends('emailTrakingViews::emails/mensaje_layout')
2
+@section('title')
3
+    Message from {{config('mail-tracker.name')}}
4
+@endsection
5
+
6
+@section('preheader')
7
+    Message from {{config('mail-tracker.name')}} <br>
8
+@endsection
9
+@section('nombre_destinatario')
10
+    {{ $data['name'] }}
11
+@endsection
12
+@section('mensaje')
13
+    <h3>Static Email Title</h3>
14
+    <p>
15
+        Static Email Content
16
+    </p>
17
+   {{ $data['message'] }}
18
+@endsection
19
+@section('href_call_to_action')
20
+    {{env('APP_URL')}}
21
+@endsection
22
+@section('txt_call_to_action')
23
+    Call To Action
24
+@endsection
25
+@section('txt_extra')
26
+    This email comes from <a href="{{env('APP_URL')}}" style="text-decoration:none; color:#4b679d;">{{config('mail-tracker.name')}}</a>
27
+@endsection
28
+@section('saludo_final')
29
+    Regards
30
+@endsection

+ 195
- 0
resources/views/vendor/emailTrakingViews/emails/mensaje_layout.blade.php Dosyayı Görüntüle

@@ -0,0 +1,195 @@
1
+<!doctype html>
2
+<html>
3
+  <head>
4
+    <meta name="viewport" content="width=device-width">
5
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6
+    <title>@yield('title')</title>
7
+    <style media="all" type="text/css">
8
+            @media all {
9
+              .btn-primary table td:hover {
10
+                background-color: #34495e !important;
11
+              }
12
+              .btn-primary a:hover {
13
+                background-color: #34495e !important;
14
+                border-color: #34495e !important;
15
+              }
16
+            }
17
+
18
+            @media all {
19
+              .btn-secondary a:hover {
20
+                border-color: #34495e !important;
21
+                color: #34495e !important;
22
+              }
23
+            }
24
+
25
+            @media only screen and (max-width: 620px) {
26
+              table[class=body] h1 {
27
+                font-size: 28px !important;
28
+                margin-bottom: 10px !important;
29
+              }
30
+              table[class=body] h2 {
31
+                font-size: 22px !important;
32
+                margin-bottom: 10px !important;
33
+              }
34
+              table[class=body] h3 {
35
+                font-size: 16px !important;
36
+                margin-bottom: 10px !important;
37
+              }
38
+              table[class=body] p,
39
+              table[class=body] ul,
40
+              table[class=body] ol,
41
+              table[class=body] td,
42
+              table[class=body] span,
43
+              table[class=body] a {
44
+                font-size: 16px !important;
45
+              }
46
+              table[class=body] .wrapper,
47
+              table[class=body] .article {
48
+                padding: 10px !important;
49
+              }
50
+              table[class=body] .content {
51
+                padding: 0 !important;
52
+              }
53
+              table[class=body] .container {
54
+                padding: 0 !important;
55
+                width: 100% !important;
56
+              }
57
+              table[class=body] .header {
58
+                margin-bottom: 10px !important;
59
+              }
60
+              table[class=body] .main {
61
+                border-left-width: 0 !important;
62
+                border-radius: 0 !important;
63
+                border-right-width: 0 !important;
64
+              }
65
+              table[class=body] .btn table {
66
+                width: 100% !important;
67
+              }
68
+              table[class=body] .btn a {
69
+                width: 100% !important;
70
+              }
71
+              table[class=body] .img-responsive {
72
+                height: auto !important;
73
+                max-width: 100% !important;
74
+                width: auto !important;
75
+              }
76
+              table[class=body] .alert td {
77
+                border-radius: 0 !important;
78
+                padding: 10px !important;
79
+              }
80
+              table[class=body] .span-2,
81
+              table[class=body] .span-3 {
82
+                max-width: none !important;
83
+                width: 100% !important;
84
+              }
85
+              table[class=body] .receipt {
86
+                width: 100% !important;
87
+              }
88
+            }
89
+
90
+            @media all {
91
+              .ExternalClass {
92
+                width: 100%;
93
+              }
94
+              .ExternalClass,
95
+              .ExternalClass p,
96
+              .ExternalClass span,
97
+              .ExternalClass font,
98
+              .ExternalClass td,
99
+              .ExternalClass div {
100
+                line-height: 100%;
101
+              }
102
+              .apple-link a {
103
+                color: inherit !important;
104
+                font-family: inherit !important;
105
+                font-size: inherit !important;
106
+                font-weight: inherit !important;
107
+                line-height: inherit !important;
108
+                text-decoration: none !important;
109
+              }
110
+            }
111
+    </style>
112
+  </head>
113
+  <body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
114
+    <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
115
+      <tr>
116
+        <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
117
+        <td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
118
+          <div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
119
+
120
+            <!-- START CENTERED WHITE CONTAINER -->
121
+            <span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">
122
+            @yield('preheader')
123
+            {{--This is preheader text. Some clients will show this text as a preview.--}}
124
+            </span>
125
+            <table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
126
+
127
+              <!-- START MAIN CONTENT AREA -->
128
+              <tr>
129
+                <td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
130
+                  <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
131
+                    <tr>
132
+                      <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" align="right">
133
+                          <a href="{{env('APP_URL')}}" style="text-decoration:none; font-size: 24px; vertical-align: top; color:#4b679d;">
134
+                              {{config('mail-tracker.name')}}
135
+                          </a>
136
+                      </td>
137
+                    </tr>
138
+                    <tr>
139
+                      <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
140
+                        <p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi @yield('nombre_destinatario')</p>
141
+                        <p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">@yield('mensaje')</p>
142
+                        <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;" width="100%">
143
+                          <tbody>
144
+                            <tr>
145
+                              <td align="left" style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;" valign="top">
146
+                                <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
147
+                                  <tbody>
148
+                                    <tr>
149
+                                      <td style="font-family: sans-serif; font-size: 14px; vertical-align: top; background-color: #3498db; border-radius: 5px; text-align: center;" valign="top" bgcolor="#3498db" align="center">
150
+                                          <a href="@yield('href_call_to_action')" target="_blank" style="display: inline-block; color: #ffffff; background-color: #3498db; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: #3498db;"> @yield('txt_call_to_action')
151
+                                          </a>
152
+                                      </td>
153
+                                    </tr>
154
+                                  </tbody>
155
+                                </table>
156
+                              </td>
157
+                            </tr>
158
+                          </tbody>
159
+                        </table>
160
+                        <p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">@yield('txt_extra')</p>
161
+                        <p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">@yield('saludo_final')</p>
162
+                      </td>
163
+                    </tr>
164
+                  </table>
165
+                </td>
166
+              </tr>
167
+
168
+              <!-- END MAIN CONTENT AREA -->
169
+              </table>
170
+
171
+            <!-- START FOOTER -->
172
+            <div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
173
+              <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
174
+                <tr>
175
+                  <td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
176
+                    <span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;">{{config('mail-tracker.name')}} - {{env('APP_URL')}}</span>.
177
+                  </td>
178
+                </tr>
179
+                <tr>
180
+                  <td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
181
+                    Email from <a href="{{env('APP_URL')}}" style="color: #999999; font-size: 12px; text-align: center; text-decoration: none;">{{config('mail-tracker.name')}}</a>.
182
+                  </td>
183
+                </tr>
184
+              </table>
185
+            </div>
186
+
187
+            <!-- END FOOTER -->
188
+
189
+<!-- END CENTERED WHITE CONTAINER --></div>
190
+        </td>
191
+        <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
192
+      </tr>
193
+    </table>
194
+  </body>
195
+</html>

+ 129
- 0
resources/views/vendor/emailTrakingViews/index.blade.php Dosyayı Görüntüle

@@ -0,0 +1,129 @@
1
+@extends('layouts/layoutMaster')
2
+
3
+@section('title', 'Mail Tracker')
4
+
5
+@section('pageTitle')
6
+<div class="d-flex flex-column">
7
+  <h4 class="mb-1 lh-1">Mail Tracker</h4>
8
+  <nav aria-label="breadcrumb">
9
+    <ol class="breadcrumb breadcrumb-custom-icon mb-0">
10
+      <li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Home</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
11
+      <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Admin</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
12
+      <li class="breadcrumb-item active">Mail Tracker</li>
13
+    </ol>
14
+  </nav>
15
+</div>
16
+@endsection
17
+
18
+@section('content')
19
+
20
+{{-- Filtri ricerca --}}
21
+<div class="card mb-4">
22
+  <div class="card-header border-bottom">
23
+    <h5 class="card-title mb-0">
24
+      <i class="bx bx-search-alt-2 text-primary me-2"></i>
25
+      Ricerca
26
+    </h5>
27
+  </div>
28
+  <div class="card-body">
29
+    <form action="{{ route('mailTracker_Search') }}" method="post" class="row g-3 align-items-end">
30
+      @csrf
31
+      <div class="col-12 col-md-6 col-lg-{{ !is_null(config('mail-tracker.search-date-start')) ? '3' : '4' }}">
32
+        <label for="search" class="form-label">Testo</label>
33
+        <input type="text" class="form-control" name="search" id="search" value="{{ session('mail-tracker-index-search') }}" placeholder="Destinatario, oggetto…">
34
+      </div>
35
+      @if(!is_null(config('mail-tracker.search-date-start')))
36
+      <div class="col-12 col-md-6 col-lg-3">
37
+        <label for="date_start" class="form-label">Invio da</label>
38
+        <input type="date" class="form-control" name="date_start" id="date_start" value="{{ session('mail-tracker-index-date-start') }}">
39
+      </div>
40
+      <div class="col-12 col-md-6 col-lg-3">
41
+        <label for="date_end" class="form-label">Invio a</label>
42
+        <input type="date" class="form-control" name="date_end" id="date_end" value="{{ session('mail-tracker-index-date-end') }}">
43
+      </div>
44
+      @endif
45
+      <div class="col-12 col-md-6 col-lg-{{ !is_null(config('mail-tracker.search-date-start')) ? '3' : '8' }} d-flex flex-wrap gap-2">
46
+        <button type="submit" class="btn btn-primary">
47
+          <i class="bx bx-filter-alt me-1"></i>
48
+          Cerca
49
+        </button>
50
+        <a href="{{ route('mailTracker_ClearSearch') }}" class="btn btn-label-secondary">
51
+          <i class="bx bx-x me-1"></i>
52
+          Azzera filtri
53
+        </a>
54
+      </div>
55
+    </form>
56
+  </div>
57
+</div>
58
+
59
+{{-- Tabella email --}}
60
+<div class="card">
61
+  <div class="card-header border-bottom d-flex flex-wrap justify-content-between align-items-center gap-2">
62
+    <h5 class="card-title mb-0">
63
+      <i class="bx bx-envelope text-primary me-2"></i>
64
+      Email tracciate
65
+    </h5>
66
+    <span class="badge bg-label-primary rounded-pill">{{ $emails->total() }} {{ $emails->total() === 1 ? 'risultato' : 'risultati' }}</span>
67
+  </div>
68
+  <div class="card-body p-0">
69
+    <div class="table-responsive">
70
+      <table class="table table-hover table-striped mb-0">
71
+        <thead class="table-light">
72
+          <tr>
73
+            <th>Destinatario</th>
74
+            <th>Oggetto</th>
75
+            <th>Prima visualizzazione</th>
76
+            <th class="text-end">Aperture</th>
77
+            <th>Primo click</th>
78
+            <th class="text-end">Click</th>
79
+            <th>Inviato il</th>
80
+            <th class="text-nowrap">Anteprima</th>
81
+            <th>Report link</th>
82
+          </tr>
83
+        </thead>
84
+        <tbody>
85
+          @forelse($emails as $email)
86
+          <tr class="{{ $email->report_class }}">
87
+            <td class="text-break">{{ \Illuminate\Support\Str::unwrap(trim((string) ($email->recipient ?? '')), '<', '>') }}</td>
88
+            <td class="text-break">{{ $email->subject }}</td>
89
+            <td class="text-nowrap small">{{ $email->opened_at ? \Illuminate\Support\Carbon::parse($email->opened_at)->format(config('mail-tracker.date-format')) : '—' }}</td>
90
+            <td class="text-end">{{ $email->opens }}</td>
91
+            <td class="text-nowrap small">{{ $email->clicked_at ? \Illuminate\Support\Carbon::parse($email->clicked_at)->format(config('mail-tracker.date-format')) : '—' }}</td>
92
+            <td class="text-end">{{ $email->clicks }}</td>
93
+            <td class="text-nowrap small">{{ $email->created_at->format(config('mail-tracker.date-format')) }}</td>
94
+            <td>
95
+              <a href="{{ route('mailTracker_ShowEmail', $email->id) }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-icon btn-label-secondary" title="Apri email" aria-label="Apri email">
96
+                <i class="bx bx-show"></i>
97
+              </a>
98
+            </td>
99
+            <td>
100
+              @if($email->clicks > 0)
101
+              <a href="{{ route('mailTracker_UrlDetail', $email->id) }}" class="btn btn-sm btn-label-primary">
102
+                <i class="bx bx-link-external me-1"></i>
103
+                URL
104
+              </a>
105
+              @else
106
+              <span class="text-muted small">—</span>
107
+              @endif
108
+            </td>
109
+          </tr>
110
+          @empty
111
+          <tr>
112
+            <td colspan="9" class="text-center text-muted py-5">
113
+              <i class="bx bx-inbox bx-lg d-block mb-2 opacity-50"></i>
114
+              Nessuna email trovata. Modifica i filtri di ricerca e riprova.
115
+            </td>
116
+          </tr>
117
+          @endforelse
118
+        </tbody>
119
+      </table>
120
+    </div>
121
+  </div>
122
+  @if($emails->hasPages())
123
+  <div class="card-footer d-flex justify-content-center border-top py-3">
124
+    {!! $emails->render() !!}
125
+  </div>
126
+  @endif
127
+</div>
128
+
129
+@endsection

+ 13
- 0
resources/views/vendor/emailTrakingViews/layouts/app.blade.php Dosyayı Görüntüle

@@ -0,0 +1,13 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+    <meta name="viewport" content="width=device-width, initial-scale=1">
7
+    <title>Mail Tracker</title>
8
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
9
+  </head>
10
+  <body>
11
+  	@yield('content')
12
+  </body>
13
+</html>

+ 1
- 0
resources/views/vendor/emailTrakingViews/show.blade.php Dosyayı Görüntüle

@@ -0,0 +1 @@
1
+{!!$email->content!!}

+ 52
- 0
resources/views/vendor/emailTrakingViews/smtp_detail.blade.php Dosyayı Görüntüle

@@ -0,0 +1,52 @@
1
+@extends('layouts/layoutMaster')
2
+
3
+@section('title', 'Mail Tracker — SMTP')
4
+
5
+@section('pageTitle')
6
+<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-start gap-2">
7
+  <div class="d-flex flex-column">
8
+    <h4 class="mb-1 lh-1">Dettaglio SMTP</h4>
9
+    <nav aria-label="breadcrumb">
10
+      <ol class="breadcrumb breadcrumb-custom-icon mb-0">
11
+        <li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Home</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
12
+        <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Admin</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
13
+        <li class="breadcrumb-item"><a href="{{ route('mailTracker_Index', ['page' => session('mail-tracker-index-page')]) }}">Mail Tracker</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
14
+        <li class="breadcrumb-item active">SMTP</li>
15
+      </ol>
16
+    </nav>
17
+  </div>
18
+  <a href="{{ route('mailTracker_Index', ['page' => session('mail-tracker-index-page')]) }}" class="btn btn-label-secondary">
19
+    <i class="bx bx-arrow-back me-1"></i>
20
+    Tutte le email
21
+  </a>
22
+</div>
23
+@endsection
24
+
25
+@section('content')
26
+
27
+<div class="card">
28
+  <div class="card-header border-bottom d-flex flex-wrap justify-content-between align-items-center gap-2">
29
+    <h5 class="card-title mb-0">
30
+      <i class="bx bx-server text-primary me-2"></i>
31
+      Email #{{ $details->id }}
32
+    </h5>
33
+    <a href="{{ route('mailTracker_ShowEmail', $details->id) }}" class="btn btn-sm btn-label-secondary" target="_blank" rel="noopener noreferrer">
34
+      <i class="bx bx-show me-1"></i>
35
+      Anteprima
36
+    </a>
37
+  </div>
38
+  <div class="card-body">
39
+    <dl class="row mb-0">
40
+      <dt class="col-sm-3">Destinatario</dt>
41
+      <dd class="col-sm-9 text-break">{{ \Illuminate\Support\Str::unwrap(trim((string) ($details->recipient ?? '')), '<', '>') }}</dd>
42
+      <dt class="col-sm-3">Oggetto</dt>
43
+      <dd class="col-sm-9 text-break">{{ $details->subject }}</dd>
44
+      <dt class="col-sm-3">Inviato il</dt>
45
+      <dd class="col-sm-9">{{ $details->created_at->format(config('mail-tracker.date-format')) }}</dd>
46
+      <dt class="col-sm-3">SMTP</dt>
47
+      <dd class="col-sm-9"><pre class="mb-0 text-wrap">{{ $details->smtp_info }}</pre></dd>
48
+    </dl>
49
+  </div>
50
+</div>
51
+
52
+@endsection

+ 96
- 0
resources/views/vendor/emailTrakingViews/url_detail.blade.php Dosyayı Görüntüle

@@ -0,0 +1,96 @@
1
+@extends('layouts/layoutMaster')
2
+
3
+@section('title', 'Mail Tracker — Report link')
4
+
5
+@php
6
+  $email = $details->first()?->email;
7
+@endphp
8
+
9
+@section('pageTitle')
10
+<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-start gap-2">
11
+  <div class="d-flex flex-column">
12
+    <h4 class="mb-1 lh-1">Report link</h4>
13
+    <nav aria-label="breadcrumb">
14
+      <ol class="breadcrumb breadcrumb-custom-icon mb-0">
15
+        <li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Home</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
16
+        <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Admin</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
17
+        <li class="breadcrumb-item"><a href="{{ route('mailTracker_Index', ['page' => session('mail-tracker-index-page')]) }}">Mail Tracker</a><i class="breadcrumb-icon icon-base bx bx-chevron-right align-middle"></i></li>
18
+        <li class="breadcrumb-item active">Report link</li>
19
+      </ol>
20
+    </nav>
21
+  </div>
22
+  <a href="{{ route('mailTracker_Index', ['page' => session('mail-tracker-index-page')]) }}" class="btn btn-label-secondary">
23
+    <i class="bx bx-arrow-back me-1"></i>
24
+    Tutte le email
25
+  </a>
26
+</div>
27
+@endsection
28
+
29
+@section('content')
30
+
31
+@if($email)
32
+<div class="card mb-4">
33
+  <div class="card-header border-bottom d-flex flex-wrap justify-content-between align-items-center gap-2">
34
+    <h5 class="card-title mb-0">
35
+      <i class="bx bx-envelope text-primary me-2"></i>
36
+      Email #{{ $email->id }}
37
+    </h5>
38
+    <a href="{{ route('mailTracker_ShowEmail', $email->id) }}" class="btn btn-sm btn-label-secondary" target="_blank" rel="noopener noreferrer">
39
+      <i class="bx bx-show me-1"></i>
40
+      Anteprima
41
+    </a>
42
+  </div>
43
+  <div class="card-body">
44
+    <dl class="row mb-0">
45
+      <dt class="col-sm-3">Destinatario</dt>
46
+      <dd class="col-sm-9 text-break">{{ \Illuminate\Support\Str::unwrap(trim((string) ($email->recipient ?? '')), '<', '>') }}</dd>
47
+      <dt class="col-sm-3">Oggetto</dt>
48
+      <dd class="col-sm-9 text-break">{{ $email->subject }}</dd>
49
+      <dt class="col-sm-3">Inviato il</dt>
50
+      <dd class="col-sm-9">{{ $email->created_at->format(config('mail-tracker.date-format')) }}</dd>
51
+    </dl>
52
+  </div>
53
+</div>
54
+
55
+<div class="card">
56
+  <div class="card-header border-bottom">
57
+    <h5 class="card-title mb-0">
58
+      <i class="bx bx-link-external text-primary me-2"></i>
59
+      URL cliccati
60
+    </h5>
61
+  </div>
62
+  <div class="card-body p-0">
63
+    <div class="table-responsive">
64
+      <table class="table table-hover table-striped mb-0">
65
+        <thead class="table-light">
66
+          <tr>
67
+            <th>URL</th>
68
+            <th class="text-end">Click</th>
69
+            <th>Primo click</th>
70
+            <th>Ultimo click</th>
71
+          </tr>
72
+        </thead>
73
+        <tbody>
74
+          @foreach($details as $detail)
75
+          <tr>
76
+            <td class="text-break">{{ $detail->url }}</td>
77
+            <td class="text-end">{{ $detail->clicks }}</td>
78
+            <td class="text-nowrap small">{{ $detail->created_at->format(config('mail-tracker.date-format')) }}</td>
79
+            <td class="text-nowrap small">{{ $detail->updated_at->format(config('mail-tracker.date-format')) }}</td>
80
+          </tr>
81
+          @endforeach
82
+        </tbody>
83
+      </table>
84
+    </div>
85
+  </div>
86
+</div>
87
+@else
88
+<div class="card">
89
+  <div class="card-body text-center text-muted py-5">
90
+    <i class="bx bx-link-alt bx-lg d-block mb-2 opacity-50"></i>
91
+    Nessun click registrato per questa email.
92
+  </div>
93
+</div>
94
+@endif
95
+
96
+@endsection

Loading…
İptal
Kaydet