Skip to content
Merged

Dev #2891

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 90 additions & 23 deletions app/Helpers/CertificatesHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,112 @@
namespace App\Helpers;

use App\CertificateParticipation;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use ZipArchive;
use Exception;

class CertificatesHelper
{
/**
* Generate participation certificates, zip them and upload to S3.
*
* @param array $names
* @param string $event_name
* @param string $event_date
* @return string|null S3 URL or null on error
*/
public static function doGenerateCertificatesOfParticipation($names, $event_name, $event_date)
{

$filenames = [];
foreach ($names as $name) {
$filenames[] = (new CertificateParticipation($name, $event_name, $event_date))->generate();
}
try {
// Generate certificates
foreach ($names as $name) {
$filename = (new CertificateParticipation($name, $event_name, $event_date))->generate();
$pdfPath = resource_path("latex/{$filename}.pdf");
if (!file_exists($pdfPath)) {
Log::error("[Certificates] Missing certificate PDF: {$pdfPath}");
continue;
}
$filenames[] = $filename;
}

//Zip based on the filenames
$zipname = 'participation-certificates-'.Str::random(10).'.zip';
$zipfull = resource_path().'/latex/'.$zipname;
if (empty($filenames)) {
Log::error('[Certificates] No certificates generated.');
return null;
}

$zip = new ZipArchive;
$zip->open($zipfull, ZipArchive::CREATE);
foreach ($filenames as $file) {
$zip->addFile(resource_path().'/latex/'.$file.'.pdf', $file.'.pdf');
}
$zip->close();
// Zip files
$zipname = 'participation-certificates-'.Str::random(10).'.zip';
$zipfull = resource_path("latex/{$zipname}");
$zip = new ZipArchive;

//Clean them All
if ($zip->open($zipfull, ZipArchive::CREATE) !== true) {
Log::error("[Certificates] Could not create zip: {$zipfull}");
return null;
}

//Upload to S3
$inputStream = Storage::disk('latex')->getDriver()->readStream($zipname);
$destination = Storage::disk('s3')->path('/participation/'.$zipname);
Storage::disk('s3')->put($destination, $inputStream);
foreach ($filenames as $file) {
$pdfPath = resource_path("latex/{$file}.pdf");
if (file_exists($pdfPath)) {
$zip->addFile($pdfPath, "{$file}.pdf");
} else {
Log::warning("[Certificates] File missing during zipping: {$pdfPath}");
}
}
$zip->close();

foreach ($filenames as $file) {
Storage::disk('latex')->delete($file.'.pdf');
}
// Upload to S3
if (!file_exists($zipfull)) {
Log::error("[Certificates] Zip file not found for upload: {$zipfull}");
return null;
}

$zipStream = fopen($zipfull, 'r');
if (!$zipStream) {
Log::error("[Certificates] Failed to open zip stream: {$zipfull}");
return null;
}

Storage::disk('latex')->delete($zipname);
$s3Path = 'participation/' . $zipname;
$upload = Storage::disk('s3')->put($s3Path, $zipStream);
fclose($zipStream);

return Storage::disk('s3')->url('participation/'.$zipname);
if (!$upload) {
Log::error("[Certificates] Upload to S3 failed: {$s3Path}");
return null;
}

// Clean up
foreach ($filenames as $file) {
$pdfPath = resource_path("latex/{$file}.pdf");
if (file_exists($pdfPath)) {
@unlink($pdfPath);
}
}

@unlink($zipfull);

return Storage::disk('s3')->url($s3Path);

} catch (Exception $e) {
Log::error("[Certificates] Generation error: " . $e->getMessage(), [
'trace' => $e->getTraceAsString()
]);
// Optional: cleanup even if error
if (isset($zipfull) && file_exists($zipfull)) {
@unlink($zipfull);
}
if (!empty($filenames)) {
foreach ($filenames as $file) {
$pdfPath = resource_path("latex/{$file}.pdf");
if (file_exists($pdfPath)) {
@unlink($pdfPath);
}
}
}
return null;
}
}
}
6 changes: 5 additions & 1 deletion app/Jobs/GenerateCertificatesOfParticipation.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ public function handle(): void
$zipUrl = CertificatesHelper::doGenerateCertificatesOfParticipation($names, $this->participation->event_name, $this->participation->event_date);

$this->participation['participation_url'] = $zipUrl;
$this->participation['status'] = 'DONE';
if ($zipUrl) {
$this->participation['status'] = 'DONE';
} else {
$this->participation['status'] = 'FAILED';
}
$this->participation->save();
}

Expand Down
1 change: 1 addition & 0 deletions public/build/assets/app-B8CarwFj.css

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion public/build/assets/app-BHrfNuJ5.css

This file was deleted.

1 change: 1 addition & 0 deletions public/build/assets/app-BlFb7lxL.css

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion public/build/assets/app-CNrk3yLd.css

This file was deleted.

1 change: 0 additions & 1 deletion public/build/assets/app-CYEMjCxX.css

This file was deleted.

1 change: 0 additions & 1 deletion public/build/assets/app-D3sm8Cgy.css

This file was deleted.

237 changes: 0 additions & 237 deletions public/build/assets/app-sqTAEKFr.js

This file was deleted.

6 changes: 3 additions & 3 deletions public/build/manifest.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"resources/assets/sass/app.scss": {
"file": "assets/app-BHrfNuJ5.css",
"file": "assets/app-BlFb7lxL.css",
"src": "resources/assets/sass/app.scss",
"isEntry": true
},
"resources/css/app.css": {
"file": "assets/app-D3sm8Cgy.css",
"file": "assets/app-B8CarwFj.css",
"src": "resources/css/app.css",
"isEntry": true
},
"resources/js/app.js": {
"file": "assets/app-PiPmzr23.js",
"file": "assets/app-BunCVcAf.js",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true,
Expand Down
30 changes: 17 additions & 13 deletions resources/js/components/AvatarForm.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
<template>
<div class="codeweek-user-avatar">
<div class="name">
<h1>{{ user.fullName }}</h1>
</div>
<div class="avatar">
<div class="actions">
<form v-if="canUpdate" method="POST" enctype="multipart/form-data">
<div class="flex flex-col tablet:flex-row tablet:items-center gap-6 tablet:gap-14">
<div class="flex">
<div class="relative">
<img :src="avatar" class="w-40 h-40 rounded-full border-4 border-solid border-dark-blue-300">
<form v-if="canUpdate" method="POST" enctype="multipart/form-data" class="absolute bottom-0 left-0">
<image-upload name="avatar" class="mr-1" @loaded="onLoad"></image-upload>
</form>
<div style="display: flex;align-items: flex-end;margin-left: -35px;">
<button class="absolute !bottom-0 !right-0 flex justify-center items-center !h-10 !w-10 !p-0 bg-[#FE6824] rounded-full !border-2 !border-solid !border-white" @click="remove" v-show="hasAvatar">
<img class="w-5 h-5" src="/images/trash.svg">
</button>
</div>
</div>
<img :src="avatar" class="codeweek-avatar-image">
<div style="display: flex;align-items: flex-end;margin-left: -35px;">
<button class="codeweek-image-button" @click="remove" v-show="hasAvatar">
<img src="/images/trash.svg">
</button>
</div>
</div>
<div>
<h1 class="text-white font-normal text-3xl tablet:font-medium tablet:text-5xl font-['Montserrat'] mb-6">{{ user.fullName }}</h1>
<p class="text-xl font-normal text-white p-0 max-md:max-w-full max-w-[864px]">
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero.
</p>
</div>
</div>
</template>
Expand All @@ -37,6 +40,7 @@ export default {

computed: {
canUpdate() {
console.log('user', this.user);
return this.$authorize(user => user.id === this.user.id);
},
hasAvatar() {
Expand Down
5 changes: 3 additions & 2 deletions resources/js/components/ImageUpload.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<template>
<div>
<input id="image" type="file" accept="image/*" @change="onChange" />
<label for="image">Choose a file</label>
Max size: 1 Mb
<label class="!flex justify-center items-center !h-10 !w-10 !p-0 !bg-dark-blue border-2 border-white" for="image">
<img class="w-5 h-5" src="/images/edit.svg" />
</label>
</div>
</template>

Expand Down
Loading
Loading