Skip to content

Login register page redesign #2537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
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
149 changes: 75 additions & 74 deletions app/CertificateExcellence.php
Original file line number Diff line number Diff line change
@@ -13,70 +13,74 @@ class CertificateExcellence
private $templateName;

private $name_of_certificate_holder;

private $email_of_certificate_holder;

private $resource_path;

private $pdflatex;

private $personalized_template_name;

private $event;

private $id;

private $edition;

private $number_of_activities;
private $type;

public function __construct($edition, $name_for_certificate, $type = 'excellence', $number_of_activities = 0)
public function __construct($edition, $name_for_certificate, $type, $number_of_activities)
{

$this->edition = $edition;
$this->name_of_certificate_holder = $name_for_certificate;
$this->email_of_certificate_holder = auth()->user()->email ?? '';
$this->personalized_template_name = $edition . '-' . auth()->id();
$this->resource_path = resource_path() . '/latex';
$this->personalized_template_name = $edition.'-'.auth()->id();
$this->resource_path = resource_path().'/latex';
$this->pdflatex = config('codeweek.pdflatex_path');
$this->id = auth()->id() . '-' . str_random(10);
$this->id = auth()->id().'-'.str_random(10);
$this->number_of_activities = $number_of_activities;
$this->type = $type ?: 'excellence';
$this->type = $type ?? 'excellence';

// e.g. "excellence-2025.tex" or "super-organiser-2025.tex"
$this->templateName = "{$this->type}-{$this->edition}.tex";

Log::info('User ID ' . auth()->id() . " generating {$this->type} certificate with name: " . $name_for_certificate);
Log::info('User ID '.auth()->id()." generating {$this->type} certificate with name: ".$name_for_certificate);
}

/**
* Generates the certificate PDF, saves to S3, cleans up temp files.
* Returns the S3 path of the generated PDF.
*/
public function generate()
{

$this->customize_and_save_latex();
$this->run_pdf_creation();
$s3path = $this->copy_to_s3();
$this->clean_temp_files();

return $s3path;

}

/**
* Clean up LaTeX artifacts for the generated file.
*/
private function clean_temp_files()
{
Storage::disk('latex')->delete($this->personalized_template_name . '.aux');
Storage::disk('latex')->delete($this->personalized_template_name . '.tex');
Storage::disk('latex')->delete($this->personalized_template_name . '.pdf');
Storage::disk('latex')->delete($this->personalized_template_name . '.log');
Storage::disk('latex')->delete($this->personalized_template_name.'.aux');
Storage::disk('latex')->delete($this->personalized_template_name.'.tex');
Storage::disk('latex')->delete($this->personalized_template_name.'.pdf');
Storage::disk('latex')->delete($this->personalized_template_name.'.log');
}

/**
* Check for Greek characters in the name.
*/
public function is_greek()
{

$split = preg_split('/[\p{Greek}]/u', $this->name_of_certificate_holder);
return (count($split) > 1);
if (count($split) > 1) {
// Log::info("Detected as Greek: " . $this->name_of_certificate_holder);
return true;
}

return false;

}

/**
* Escape LaTeX special characters in user data.
*/
private function tex_escape($string)
{
$map = [
@@ -92,85 +96,82 @@ private function tex_escape($string)
'}' => '\\}',
];

return preg_replace_callback(
"/([\^\%~\\\\#\$%&_\{\}])/",
return preg_replace_callback("/([\^\%~\\\\#\$%&_\{\}])/",
function ($matches) use ($map) {
foreach ($matches as $match) {
return $map[$match];
}
},
$string
);
}, $string);
}

protected function update_event($s3path)
{
$this->event->update([
'certificate_url' => $s3path,
'certificate_generated_at' => Carbon::now(),
]);
}

/**
* Read the base template from disk, replace placeholders, and save the .tex file.
* @throws \League\Flysystem\FileNotFoundException
*/
protected function copy_to_s3(): string
{
$inputStream = Storage::disk('latex')->getDriver()->readStream($this->personalized_template_name.'.pdf');
$destination = Storage::disk('s3')->path('/certificates/'.$this->id.'.pdf');
Storage::disk('s3')->put($destination, $inputStream);

return Storage::disk('s3')->url('certificates/'.$this->id.'.pdf');
}

/**
* @return mixed
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function customize_and_save_latex()
{
// If the name is Greek, switch to Greek template if it exists:
if ($this->is_greek()) {
$this->templateName = "{$this->type}_greek-{$this->edition}.tex";
}

Log::info("Using template: {$this->templateName}");
Log::info($this->templateName);
//open the latex template
$base_template = Storage::disk('latex')->get($this->templateName);

// Always replace <CERTIFICATE_HOLDER_NAME> for both excellence & super-organiser
$template = str_replace(
'<CERTIFICATE_HOLDER_NAME>',
$this->tex_escape($this->name_of_certificate_holder),
$base_template
);
//replace the text in template
$template = str_replace('<CERTIFICATE_HOLDER_NAME>', $this->tex_escape($this->name_of_certificate_holder), $base_template);

// If super-organiser, we also replace these:
if ($this->type === 'super-organiser') {
// Possibly also <EDITION> if you want it dynamic
$template = str_replace('<NUMBER_OF_ACTIVITIES>', $this->tex_escape($this->number_of_activities), $template);
$template = str_replace('<CERTIFICATE_DATE>', $this->tex_escape(Carbon::now()->format('d/m/Y')), $template);
// If you added <CERTIFICATE_EMAIL> or <EDITION> placeholders, handle them as well:
if ($this->type == 'super-organiser') {
$template = str_replace('<CERTIFICATE_EMAIL>', $this->tex_escape($this->email_of_certificate_holder), $template);
$template = str_replace('<EDITION>', $this->edition, $template);
$template = str_replace('<CERTIFICATE_DATE>', $this->tex_escape(Carbon::now()->format('d/m/Y')), $template);
$template = str_replace('<NUMBER_OF_ACTIVITIES>', $this->tex_escape($this->number_of_activities), $template);
}

// For excellence type, you can do other placeholder replacements here if needed.

// Save updated .tex
Log::info($template);
Storage::disk('latex')->put($this->personalized_template_name . '.tex', $template);

//save it locally
Storage::disk('latex')->put($this->personalized_template_name.'.tex', $template);
}

/**
* Compile the .tex file with pdflatex.
*/
protected function run_pdf_creation(): void
{
$command = $this->pdflatex . ' -interaction=nonstopmode -output-directory ' .
$this->resource_path . ' ' .
$this->resource_path . '/' . $this->personalized_template_name . '.tex';

Log::info("pdflatex command: $command");
//call the pdflatex command
$command = $this->pdflatex.' -interaction=nonstopmode -output-directory '.$this->resource_path.' '.$this->resource_path.'/'.$this->personalized_template_name.'.tex';

Log::info($command);

$cwd = $this->resource_path;

Log::info($cwd);

// $process = new Process($command, $cwd);
$process = Process::fromShellCommandline($command, $cwd);
$process->run();

if (!$process->isSuccessful()) {
// executes after the command finishes
if (! $process->isSuccessful()) {
throw new ProcessFailedException($process);
}
}

/**
* Copy the resulting PDF to S3, return its S3 URL.
*/
protected function copy_to_s3(): string
{
$pdfFile = $this->personalized_template_name . '.pdf';
$inputStream = Storage::disk('latex')->getDriver()->readStream($pdfFile);
$destination = Storage::disk('s3')->path('/certificates/' . $this->id . '.pdf');

Storage::disk('s3')->put($destination, $inputStream);

return Storage::disk('s3')->url('certificates/' . $this->id . '.pdf');
}
}
1 change: 1 addition & 0 deletions app/Console/Commands/Excellence.php
Original file line number Diff line number Diff line change
@@ -63,5 +63,6 @@ public function handle(): void
Log::info($ex->getMessage());
}
}

}
}
36 changes: 9 additions & 27 deletions app/Console/Commands/NotifySuperOrganisers.php
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@ class NotifySuperOrganisers extends Command

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
@@ -37,50 +39,30 @@ public function __construct()
*/
public function handle(): void
{

$edition = $this->argument('edition');
$this->info("🔹 NotifySuperOrganisers command started for edition: $edition");

// Fetch winners for the given edition
$winners = Excellence::byYear($edition, 'SuperOrganiser');

if ($winners->isEmpty()) {
$this->warn("⚠️ No winners found for edition: $edition");
Log::info("No SuperOrganiser winners found for edition: $edition.");
return;
}

$notifiedCount = 0;

foreach ($winners as $winner) {

$user = $winner->user;

if (! $user) {
$this->warn("⚠️ Skipping winner with missing user data (ID: $winner->id)");
Log::warning("Skipping winner with missing user data. Winner ID: $winner->id");
continue;
}

if ($user->email && $user->receive_emails) {
// Send email notification
Mail::to($user->email)->queue(new \App\Mail\NotifySuperOrganiser($user, $edition));

// Mark as notified
$excellence = $user->superOrganisers->where('edition', '=', $edition)->first();
if ($excellence) {
$excellence->notified_at = Carbon::now();
$excellence->save();
}

$notifiedCount++;
$this->info("✅ Email queued for: {$user->email}");
Log::info("Email queued for SuperOrganiser: {$user->email}, Edition: $edition");
$excellence->notified_at = Carbon::now();
$excellence->save();

} else {
$this->warn("⚠️ Skipping user {$user->id} (No valid email or opted out)");
Log::info("User {$user->id} skipped - No valid email or opted out.");
Log::info($user->id.' has no valid email address');
}
}

$this->info("✅ NotifySuperOrganisers command executed for edition: $edition, Emails sent: $notifiedCount");
Log::info("NotifySuperOrganisers command completed. Edition: $edition, Emails sent: $notifiedCount.");
}
}
}
37 changes: 10 additions & 27 deletions app/Console/Commands/NotifyWinners.php
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@ class NotifyWinners extends Command

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
@@ -37,46 +39,27 @@ public function __construct()
*/
public function handle(): void
{
$edition = $this->argument('edition');

$this->info("🔹 NotifyWinners command started for edition: {$edition}");
$edition = $this->argument('edition');

// Get all Excellence rows for this edition that have notified_at=null
$winners = Excellence::byYear($edition);

if ($winners->isEmpty()) {
$this->warn("⚠️ No winners found for edition: {$edition}");
return;
}

$count = 0;
foreach ($winners as $winner) {

$user = $winner->user;

// If no user is associated, we remove this row
if (is_null($user)) {
$this->warn("User is null for winner ID {$winner->id}. Deleting row.");
$winner->delete();
continue;
}

// If user has a valid email and is set to receive emails
if ($user->email && $user->receive_emails === 1) {
} elseif ($user->email && $user->receive_emails === 1) {
Mail::to($user->email)->queue(new \App\Mail\NotifyWinner($user, $edition));
$excellence = $user->excellences->where('edition', '=', $edition)->first();
$excellence->notified_at = Carbon::now();
$excellence->save();

// Mark notified
$winner->notified_at = Carbon::now();
$winner->save();

$count++;
$this->info("✅ Queued email for: {$user->email}");
} else {
// user->email is null or user->receive_emails=0
Log::info("User {$user->id} has no valid email or doesn't receive emails");
$this->warn("User {$user->id} not notified (invalid email or receive_emails=0)");
Log::info($user->id.' has no valid email address');
}
}

$this->info("✅ NotifyWinners command finished for edition {$edition}. Emails sent: {$count}");
}
}
}
Loading