This commit is contained in:
Martin Folkerts 2023-12-05 13:55:11 +01:00
parent 486476d326
commit f60fb7e46d
21 changed files with 663 additions and 53 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpTestFrameworkVersionCache">
<tools_cache>
<tool tool_name="PHPUnit">
<cache>
<versions>
<info id="Local/Users/mfolkerts/smartCane/laravel_app/vendor/autoload.php" version="10.4.1" />
</versions>
</cache>
</tool>
</tools_cache>
</component>
</project>

View file

@ -3,15 +3,23 @@
namespace App\Livewire\Projects;
use App\Models\Project;
use App\Models\ProjectMailing;
use Livewire\Component;
class MailingManager extends Component
{
public $project;
public $mailingDetailsModal = false;
public $formData = [
];
public $active_mailing = null;
public function mount(Project $project)
{
$this->project = $project;
$this->resetFormData();
}
public function render()
@ -20,4 +28,30 @@ public function render()
'mailings' => $this->project->mailings()->orderBy('created_at', 'desc')->get(),
]);
}
public function showMailingDetailsModal(ProjectMailing $mailing)
{
$this->formData = $mailing->toArray();
$this->formData['attachments'] = $mailing->attachments->toArray();
$this->formData['recipients'] = $mailing->recipients->toArray();
$this->mailingDetailsModal = true;
}
public function closeMailingDetailsModal()
{
$this->mailingDetailsModal = false;
$this->resetFormData();
}
private function resetFormData()
{
$this->formData = [
'subject' => '',
'message' => '',
'created_at' => '',
'attachments' => [],
'recipients' => [],
];
}
}

View file

@ -81,12 +81,9 @@ public function createReport()
$myOutput[] = $buffer;
logger($buffer);
});
logger('done');
$this->processOutput = collect($myOutput)->join('\n');
} catch (ProcessFailedException $exception) {
logger('mee------------meee');
logger ('error', $exception->getMessage());
echo $exception->getMessage();
}

View file

@ -80,6 +80,13 @@ protected static function boot()
});
}
public function getAttachmentPathAttribute()
{
return storage_path(sprintf('%s/attachments', $this->download_path));
return '/storage/' . $this->download_path . '/attachments';
}
public function boundingBoxes()
{
return $this->hasMany(ProjectBoundingBox::class);

View file

@ -4,6 +4,9 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Str;
class ProjectMailing extends Model
{
@ -14,6 +17,24 @@ class ProjectMailing extends Model
'message',
];
public function addAttachment($name, UploadedFile $file)
{
$prefix = Str::random(10);
$originalFileName = $file->getClientOriginalName();
$extension = pathinfo($originalFileName, PATHINFO_EXTENSION);
$newFileName = $prefix . '_' . pathinfo($originalFileName, PATHINFO_FILENAME) . '.' . $extension;
$path = Storage::disk('local')->putFileAs(
$this->project->attachmentPath, $file, $newFileName
);
$this->attachments()->save(new ProjectMailingAttachment([
'name' => $name,
'path' => $path
]));
}
public function project()
{
return $this->belongsTo(Project::class);

View file

@ -0,0 +1,65 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Telescope::night();
$this->hideSensitiveRequestDetails();
Telescope::filter(function (IncomingEntry $entry) {
if ($this->app->environment('local')) {
return true;
}
return $entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}
/**
* Prevent sensitive request details from being logged by Telescope.
*/
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters(['_token']);
Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}
/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewTelescope', function ($user) {
return in_array($user->email, [
//
]);
});
}
}

View file

@ -10,6 +10,7 @@
"laravel/framework": "^10.10",
"laravel/jetstream": "^4.0",
"laravel/sanctum": "^3.2",
"laravel/telescope": "^4.17",
"laravel/tinker": "^2.8",
"livewire/livewire": "^3.0"
},

View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b62515a829697c05a18890e60649eba2",
"content-hash": "7b0e91d72394f3dfe615fb8207663789",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -1725,6 +1725,77 @@
},
"time": "2023-07-14T13:56:28+00:00"
},
{
"name": "laravel/telescope",
"version": "v4.17.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/telescope.git",
"reference": "64da53ee46b99ef328458eaed32202b51e325a11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/telescope/zipball/64da53ee46b99ef328458eaed32202b51e325a11",
"reference": "64da53ee46b99ef328458eaed32202b51e325a11",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel/framework": "^8.37|^9.0|^10.0",
"php": "^8.0",
"symfony/var-dumper": "^5.0|^6.0"
},
"require-dev": {
"ext-gd": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"laravel/octane": "^1.4",
"orchestra/testbench": "^6.0|^7.0|^8.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Telescope\\TelescopeServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Telescope\\": "src/",
"Laravel\\Telescope\\Database\\Factories\\": "database/factories/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
},
{
"name": "Mohamed Said",
"email": "mohamed@laravel.com"
}
],
"description": "An elegant debug assistant for the Laravel framework.",
"keywords": [
"debugging",
"laravel",
"monitoring"
],
"support": {
"issues": "https://github.com/laravel/telescope/issues",
"source": "https://github.com/laravel/telescope/tree/v4.17.2"
},
"time": "2023-11-01T14:01:06+00:00"
},
{
"name": "laravel/tinker",
"version": "v2.8.2",

View file

@ -168,6 +168,7 @@
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\TelescopeServiceProvider::class,
App\Providers\FortifyServiceProvider::class,
App\Providers\JetstreamServiceProvider::class,
])->toArray(),

View file

@ -0,0 +1,187 @@
<?php
use Laravel\Telescope\Http\Middleware\Authorize;
use Laravel\Telescope\Watchers;
return [
/*
|--------------------------------------------------------------------------
| Telescope Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Telescope will be accessible from. If the
| setting is null, Telescope will reside under the same domain as the
| application. Otherwise, this value will be used as the subdomain.
|
*/
'domain' => env('TELESCOPE_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Telescope Path
|--------------------------------------------------------------------------
|
| This is the URI path where Telescope will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => env('TELESCOPE_PATH', 'telescope'),
/*
|--------------------------------------------------------------------------
| Telescope Storage Driver
|--------------------------------------------------------------------------
|
| This configuration options determines the storage driver that will
| be used to store Telescope's data. In addition, you may set any
| custom options as needed by the particular driver you choose.
|
*/
'driver' => env('TELESCOPE_DRIVER', 'database'),
'storage' => [
'database' => [
'connection' => env('DB_CONNECTION', 'mysql'),
'chunk' => 1000,
],
],
/*
|--------------------------------------------------------------------------
| Telescope Master Switch
|--------------------------------------------------------------------------
|
| This option may be used to disable all Telescope watchers regardless
| of their individual configuration, which simply provides a single
| and convenient way to enable or disable Telescope data storage.
|
*/
'enabled' => env('TELESCOPE_ENABLED', true),
/*
|--------------------------------------------------------------------------
| Telescope Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to every Telescope route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => [
'web',
Authorize::class,
],
/*
|--------------------------------------------------------------------------
| Allowed / Ignored Paths & Commands
|--------------------------------------------------------------------------
|
| The following array lists the URI paths and Artisan commands that will
| not be watched by Telescope. In addition to this list, some Laravel
| commands, like migrations and queue commands, are always ignored.
|
*/
'only_paths' => [
// 'api/*'
],
'ignore_paths' => [
'nova-api*',
],
'ignore_commands' => [
//
],
/*
|--------------------------------------------------------------------------
| Telescope Watchers
|--------------------------------------------------------------------------
|
| The following array lists the "watchers" that will be registered with
| Telescope. The watchers gather the application's profile data when
| a request or task is executed. Feel free to customize this list.
|
*/
'watchers' => [
Watchers\BatchWatcher::class => env('TELESCOPE_BATCH_WATCHER', true),
Watchers\CacheWatcher::class => [
'enabled' => env('TELESCOPE_CACHE_WATCHER', true),
'hidden' => [],
],
Watchers\ClientRequestWatcher::class => env('TELESCOPE_CLIENT_REQUEST_WATCHER', true),
Watchers\CommandWatcher::class => [
'enabled' => env('TELESCOPE_COMMAND_WATCHER', true),
'ignore' => [],
],
Watchers\DumpWatcher::class => [
'enabled' => env('TELESCOPE_DUMP_WATCHER', true),
'always' => env('TELESCOPE_DUMP_WATCHER_ALWAYS', false),
],
Watchers\EventWatcher::class => [
'enabled' => env('TELESCOPE_EVENT_WATCHER', true),
'ignore' => [],
],
Watchers\ExceptionWatcher::class => env('TELESCOPE_EXCEPTION_WATCHER', true),
Watchers\GateWatcher::class => [
'enabled' => env('TELESCOPE_GATE_WATCHER', true),
'ignore_abilities' => [],
'ignore_packages' => true,
'ignore_paths' => [],
],
Watchers\JobWatcher::class => env('TELESCOPE_JOB_WATCHER', true),
Watchers\LogWatcher::class => [
'enabled' => env('TELESCOPE_LOG_WATCHER', true),
'level' => 'error',
],
Watchers\MailWatcher::class => env('TELESCOPE_MAIL_WATCHER', true),
Watchers\ModelWatcher::class => [
'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
'events' => ['eloquent.*'],
'hydrations' => true,
],
Watchers\NotificationWatcher::class => env('TELESCOPE_NOTIFICATION_WATCHER', true),
Watchers\QueryWatcher::class => [
'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
'ignore_packages' => true,
'ignore_paths' => [],
'slow' => 100,
],
Watchers\RedisWatcher::class => env('TELESCOPE_REDIS_WATCHER', true),
Watchers\RequestWatcher::class => [
'enabled' => env('TELESCOPE_REQUEST_WATCHER', true),
'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
'ignore_http_methods' => [],
'ignore_status_codes' => [],
],
Watchers\ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true),
Watchers\ViewWatcher::class => env('TELESCOPE_VIEW_WATCHER', true),
],
];

View file

@ -0,0 +1,43 @@
<?php
namespace Database\Factories;
use App\Models\Project;
use App\Models\Team;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
use Laravel\Jetstream\Features;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class ProjectFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => $this->faker->name,
'mail_template' => $this->faker->text,
'mail_subject' => $this->faker->word,
'mail_frequency' => 'Weekly',
'mail_day' => 'Friday',
];
}
public function configure()
{
return $this->afterMaking(function (Project $project) {
if (empty($project->download_path)) {
$project->download_path = Str::snake(Str::lower($project->name));
}
})->afterCreating(function (Project $project) {
// Eventuele aanvullende acties na het maken van het model
});
}
}

View file

@ -103,6 +103,19 @@ private function createChembaProject()
'updated_at' => $mailing->updated_at,
],
]);
$mailing->attachments()->createMany([
[
'name' => 'Chemba 2021-01-01',
'path' => 'chemba/2021-01-01',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
], [
'name' => 'Chemba 2021-01-08',
'path' => 'chemba/2021-01-08',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
],]);
}
}
@ -182,6 +195,19 @@ private function createXinavaneProject()
'updated_at' => $mailing->updated_at,
],
]);
$mailing->attachments()->createMany([
[
'name' => 'Xinavane 2021-01-01',
'path' => 'xinavane/2021-01-01',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
], [
'name' => 'Xinavane 2021-01-08',
'path' => 'xinavane/2021-01-08',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
],]);
}
}
@ -252,6 +278,19 @@ private function createKakiraProject()
'updated_at' => $mailing->updated_at,
],
]);
$mailing->attachments()->createMany([
[
'name' => 'Kakira 2021-01-01',
'path' => 'kakira/2021-01-01',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
], [
'name' => 'Kakira 2021-01-08',
'path' => 'kakira/2021-01-08',
'created_at' => $mailing->created_at,
'updated_at' => $mailing->updated_at,
],
]);
}
}
}

View file

@ -21,8 +21,8 @@
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,5 @@
{
"/app.js": "/app.js?id=140ed4bc5b10bc99492b97668c59272d",
"/app-dark.css": "/app-dark.css?id=b11fa9a28e9d3aeb8c92986f319b3c44",
"/app.css": "/app.css?id=b3ccfbe68f24cff776f83faa8dead721"
}

View file

@ -13,16 +13,6 @@
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<script>
document.addEventListener('livewire:init', () => {
Livewire.hook('request', (object) => {
debugger;
})
})
</script>
<!-- Styles -->
@livewireStyles
</head>

View file

@ -1,4 +1,5 @@
<x-tab-section>
<div>
<x-tab-section>
<x-slot name="title">Mailing Details</x-slot>
<x-slot name="description">...</x-slot>
@ -7,12 +8,14 @@
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">Id
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">
Id
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Created</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Subject</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">#</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Attachment</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Attachment
</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
<span class="sr-only">Edit</span>
</th>
@ -22,19 +25,83 @@
@foreach($project->mailings as $mail)
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">{{ $mail->id }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->created_at }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->created_at }} </td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->subject }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->recipients()->count() }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->attachments()->get('name')->implode(', ') }}</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $mail->attachments()->pluck('name')->join( ', ') }}</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
<button type="button" class="text-indigo-600 hover:text-indigo-900">Show</button>
<button type="button" wire:click="showMailingDetailsModal({{ $mail->id }})"
class="text-indigo-600 hover:text-indigo-900">Show
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</tbody>
</table>
</x-slot>
</x-tab-section>
</x-tab-section>
<x-modal wire:model.live="mailingDetailsModal">
<x-form-modal submit="saveProject">
<x-slot name="title">
{{ __('Mailing') }}
</x-slot>
<x-slot name="description">
<x-label for="created_at" value="{{ __('Created') }}"/>
<x-label id="created_at" value="{{ \Carbon\Carbon::parse($formData['created_at'])->format('Y-m-d H:i') }}"/>
</x-slot>
<x-slot name="form">
<div class="col-span-6">
<x-label for="recipients" value="{{ __('Recipients') }}"/>
@foreach($formData['recipients'] as $key => $recipient)
<div class="col-span-6 sm:col-span-4">
<x-label class="inline-block" for="recipients" value="{{ $recipient['name'] }}"/>
<x-label class="inline-block" for="recipients" value="<{{ $recipient['email'] }}>"/>
</div>
@endforeach
</div>
<div class="col-span-6">
<x-label for="subject" value="{{ __('Subject') }}"/>
<x-input id="subject" type="text" class="mt-1 block w-full" disabled wire:model="formData.subject"/>
</div>
<div class="col-span-6">
<x-label for="message" value="{{ __('Message') }}"/>
<textarea
id="message"
type="text"
class="mt-1 block w-full"
wire:model="formData.message"
disabled
></textarea>
</div>
@empty($formData['attachments'])
<div class="col-span-6">
<x-label for="message" value="{{ __('Attachment') }}"/>
<x-label class="inline-block" for=""
value="{{ __('No attachments where send with this message.') }}"/>
</div>
@else
@foreach($formData['attachments'] as $key => $attachment)
<div class="col-span-6">
<x-label class="inline-block" for="recipients" value="{{ $attachment['name'] }}"/>
</div>
@endforeach
@endempty
</x-slot>
<x-slot name="actions">
<x-secondary-button class="mr-3"
type="button"
wire:click="closeMailingDetailsModal"
>
{{ __('Close') }}
</x-secondary-button>
</x-slot>
</x-form-modal>
</x-modal>
</div>

View file

@ -0,0 +1,51 @@
<?php
namespace Tests\Unit\Models;
use App\Models\Project;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProjectTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
}
/** @test */
public function it_should_return_the_correct_attachment_path()
{
$project = Project::factory()->create(['name' => 'Test Project']);
$this->assertStringEndsWith(
'/storage/test_project/attachments',
$project->attachment_path
);
}
/** @test */
public function it_can_add_a_mailing_and_attachment()
{
$project = Project::factory()->create();
$project->mailings()->create([
'subject' => 'Test Subject',
'message' => 'Test Message',
]);
$fileFake = \Illuminate\Http\UploadedFile::fake()->create('test.pdf');
$project->mailings()->first()->addAttachment('test.pdf', $fileFake);
$this->assertCount(1, $project->mailings);
$this->assertCount(1, $project->mailings()->first()->attachments);
}
/** @test */
public function when_running_the_seeder_their_are_three_projects(): void
{
$this->seed();
$this->assertCount(3,Project::all());
}
}