Skip to content
Merged
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
7 changes: 7 additions & 0 deletions app/Achievements/AchievementsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ class AchievementsServiceProvider extends ServiceProvider
Types\InfluencerExpert2024::class,
Types\InfluencerChampion2024::class,
Types\InfluencerLegendary2024::class,
Types\OrganiserActive2026::class,
Types\OrganiserExpert2026::class,
Types\Influencer2026::class,
Types\InfluencerActive2026::class,
Types\InfluencerExpert2026::class,
Types\InfluencerChampion2026::class,
Types\InfluencerLegendary2026::class,

];

Expand Down
23 changes: 23 additions & 0 deletions app/Achievements/Types/Influencer2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class Influencer2026 extends AchievementType
{
public $icon = 'influencer/influencer_normal_2026.png';

public $edition = 2026;

public $name = 'Influencer 2026';

public function description()
{
return 'Spread your influence more to get 10 bits';
}

public function qualifier($user)
{
return $user->influence($this->edition) >= 10;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/InfluencerActive2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class InfluencerActive2026 extends AchievementType
{
public $icon = 'influencer/influencer_active_2026.png';

public $edition = 2026;

public $name = 'Active Influencer 2026';

public function description()
{
return 'Spread your influence more to get 20 bits';
}

public function qualifier($user)
{
return $user->influence($this->edition) >= 20;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/InfluencerChampion2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class InfluencerChampion2026 extends AchievementType
{
public $icon = 'influencer/influencer_champion_2026.png';

public $edition = 2026;

public $name = 'Champion Influencer 2026';

public function description()
{
return 'Spread your influence more to get 40 bits';
}

public function qualifier($user)
{
return $user->influence($this->edition) >= 40;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/InfluencerExpert2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class InfluencerExpert2026 extends AchievementType
{
public $icon = 'influencer/influencer_expert_2026.png';

public $edition = 2026;

public $name = 'Expert Influencer 2026';

public function description()
{
return 'Spread your influence more to get 30 bits';
}

public function qualifier($user)
{
return $user->influence($this->edition) >= 30;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/InfluencerLegendary2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class InfluencerLegendary2026 extends AchievementType
{
public $icon = 'influencer/influencer_legendary_2026.png';

public $edition = 2026;

public $name = 'Legendary Influencer 2026';

public function description()
{
return 'Spread your influence more to get 60 bits';
}

public function qualifier($user)
{
return $user->influence($this->edition) >= 60;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/OrganiserActive2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class OrganiserActive2026 extends AchievementType
{
public $icon = 'organiser/organiser_active_2026.png';

public $edition = 2026;

public $name = 'Active Organiser 2026';

public function description()
{
return 'Report 5 activities to unlock';
}

public function qualifier($user)
{
return $user->reported($this->edition) >= 5;
}
}

23 changes: 23 additions & 0 deletions app/Achievements/Types/OrganiserExpert2026.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Achievements\Types;

class OrganiserExpert2026 extends AchievementType
{
public $icon = 'organiser/organiser_expert_2026.png';

public $edition = 2026;

public $name = 'Expert Organiser 2026';

public function description()
{
return 'Report 10 activities to unlock';
}

public function qualifier($user)
{
return $user->reported($this->edition) >= 10;
}
}

129 changes: 129 additions & 0 deletions app/Certificates/SuperOrganiserCertificate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

namespace App\Certificates;

use App\Traits\LanguageDetection;
use App\Traits\LatexCleaner;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;

class SuperOrganiserCertificate
{
use LanguageDetection, LatexCleaner;

private string $templateName;
private string $name_of_certificate_holder;
private int $number_of_activities;
private string $certificate_date;
private string $language;

private string $resource_path;
private string $pdflatex;
private string $personalized_template_name;
private string $id;

public function __construct(string $holderName, int $numberOfActivities, ?string $certificateDate = null, string $language = 'en')
{
ini_set('max_execution_time', '600');
ini_set('memory_limit', '512M');

$this->name_of_certificate_holder = $holderName;
$this->number_of_activities = $numberOfActivities;
$this->certificate_date = $certificateDate ?: Carbon::now()->format('d/m/Y');
$this->language = $language;

$this->templateName = ($language === 'el')
? 'super-organiser-greek-2025.tex'
: 'super-organiser-2025.tex';

$random = Str::random(10);
$this->personalized_template_name = $random.'-'.'0';

$this->resource_path = resource_path('latex');
$this->pdflatex = config('codeweek.pdflatex_path', '/Library/TeX/texbin/pdflatex');
$this->id = '0-'.$random;
}

public function generate(): string
{
$this->customize_and_save_latex();
$this->run_pdf_creation();
$s3path = $this->upload_to_s3();
$this->clean_temp_files();

return $s3path;
}

private function clean_temp_files(): void
{
foreach (['aux', 'log', 'out', 'tex'] as $ext) {
Storage::disk('latex')->delete($this->personalized_template_name.'.'.$ext);
}
}

protected function customize_and_save_latex(): void
{
$base_template = Storage::disk('latex')->get($this->templateName);

$template = str_replace('<CERTIFICATE_HOLDER_NAME>', $this->tex_escape($this->name_of_certificate_holder), $base_template);
$template = str_replace('<NUMBER_OF_ACTIVITIES>', $this->tex_escape((string) $this->number_of_activities), $template);
$template = str_replace('<CERTIFICATE_DATE>', $this->tex_escape($this->certificate_date), $template);

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

protected function run_pdf_creation(): void
{
if (!file_exists($this->pdflatex)) {
throw new \RuntimeException("pdflatex binary not found at path: {$this->pdflatex}");
}

$texFile = $this->resource_path.'/'.$this->personalized_template_name.'.tex';
$command = sprintf(
'%s -interaction=nonstopmode -output-directory %s %s',
escapeshellcmd($this->pdflatex),
escapeshellarg($this->resource_path),
escapeshellarg($texFile)
);

$process = Process::fromShellCommandline($command, $this->resource_path);
$process->setTimeout(600);
$process->run();
}

protected function upload_to_s3(): string
{
$pdfFile = $this->personalized_template_name.'.pdf';
$localPath = storage_path('app/latex/'.$pdfFile);
$s3Path = 'certificates/super-organiser/'.$pdfFile;

Storage::disk('s3')->put($s3Path, file_get_contents($localPath), 'public');

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

private function tex_escape(string $string): string
{
$map = [
'#' => '\\#',
'$' => '\\$',
'%' => '\\%',
'&' => '\\&',
'~' => '\\~{}',
'_' => '\\_',
'^' => '\\^{}',
'\\' => '\\textbackslash{}',
'{' => '\\{',
'}' => '\\}',
];

return preg_replace_callback(
"/([\\^\\%~\\\\#\\$%&_\\{\\}])/",
fn ($matches) => $map[$matches[0]] ?? $matches[0],
$string
) ?? $string;
}
}

30 changes: 0 additions & 30 deletions app/Console/Commands/Kernel.php

This file was deleted.

Loading
Loading