Skip to content

Certificates for community service#791

Open
viktorcsimma wants to merge 15 commits intodevelopmentfrom
certs-for-service
Open

Certificates for community service#791
viktorcsimma wants to merge 15 commits intodevelopmentfrom
certs-for-service

Conversation

@viktorcsimma
Copy link
Copy Markdown
Contributor

@viktorcsimma viktorcsimma commented Feb 19, 2026

Closes #782.

  • Approved requests got a 'Download' button which generates a certificate in LaTeX.
  • There is an optional date field (as a string, so that not-so-concrete dates can also be given, like 'beginning of March 2026'). If it's not filled, its place simply gets omitted from the certificate.
  • Now the secretariat can also receive requests (in their case, the name of the approver is replaced with that of the director instead). UPDATE: requests to the secretariat are made on the Documents page.

UPDATE: the new screenshots and examples:

kérelem jóváhagyás

mintaigazolas.pdf

@viktorcsimma viktorcsimma self-assigned this Feb 19, 2026
@viktorcsimma viktorcsimma requested a review from a team as a code owner February 19, 2026 20:42
@viktorcsimma viktorcsimma changed the title Certs for service Certificates for community service Feb 19, 2026
This commit fixes the style issues introduced in 3881860 according to the output
from PHP CS Fixer.

Details: #791
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 19, 2026

📝 Walkthrough

Walkthrough

Adds LaTeX-based PDF certificate generation for community service entries, consolidates sanitization and PDF rendering into a new LatexHelper, adds optional date_of_service (migration + model fillable), introduces certificate generation endpoint and policy, updates controllers/views/routes and several LaTeX templates, and minor config/ui tweaks.

Changes

Cohort / File(s) Summary
LaTeX Utility & Template
app/Utils/LatexHelper.php, resources/views/latex/community-service.blade.php
Adds LatexHelper (sanitize + generatePDF) and a new LaTeX certificate template for community-service PDFs.
Sanitizer removed
app/Utils/LatexSanitizer.php
Removes the old sanitizer class; functionality migrated into LatexHelper.
Blade templates updated to use helper
resources/views/latex/departure-statement.blade.php, resources/views/latex/import.blade.php, resources/views/latex/register-statement.blade.php, resources/views/latex/status-cert.blade.php
Replaces LatexSanitizer::sanitizeLatex calls with LatexHelper::sanitizeLatex.
Community Service model & DB
app/Models/CommunityService.php, database/migrations/..._community_service_add_date_of_service.php
Adds date_of_service to $fillable and introduces migration to add nullable date_of_service text column.
Student Council controller + policy
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php, app/Policies/CommunityServicePolicy.php
Adds generateCertificate action, approvingRole helper, and policy generateCertificate; extends approval role checks to include SECRETARY.
Role accessor
app/Models/Role.php
Adds Role::secretary() accessor.
Secretariat controller & view changes
app/Http/Controllers/Secretariat/DocumentController.php, resources/views/secretariat/document/index.blade.php
Swaps PDF generation to LatexHelper, adjusts index(Request $request) data flow and role-aware UI rendering.
Student-council UI updates
resources/views/student-council/community-service/request.blade.php, resources/views/student-council/community-service/table.blade.php, resources/views/student-council/community-service/app.blade.php
Adds date_of_service input, table column, UI/card adjustments, and certificate-generation form/action when authorised.
Routing & misc
routes/web.php, app/Console/Commands.php, config/database.php, .gitignore
Adds POST route for certificate generation, fixes a docblock typo, toggles MySQL strict=false, small Redis prefix formatting change, and adds composer.phar to .gitignore.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant Controller as CommunityServiceController
    participant Policy as CommunityServicePolicy
    participant Model as CommunityService
    participant Helper as LatexHelper
    participant Storage as LaTeX_Storage
    participant PDFEngine as Commands::latexToPdf

    User->>Browser: Click "Generate Certificate"
    Browser->>Controller: POST /community_service/generate_certificate/{id}
    Controller->>Policy: authorize(generateCertificate, service)
    Policy->>Model: verify service.approved && user is approver
    alt allowed
        Controller->>Model: load service, requester, approver
        Controller->>Controller: determine approvingRole/approver_name/title
        Controller->>Helper: generatePDF(view, data)
        Helper->>Helper: sanitize data & render .tex
        Helper->>Storage: write .tex file
        Helper->>PDFEngine: latexToPdf(.tex, outputDir)
        PDFEngine-->>Helper: .pdf path
        Helper-->>Controller: return file path
        Controller-->>Browser: file download response
    else denied
        Controller-->>Browser: 403 Forbidden
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement

Suggested reviewers

  • horcsinbalint
  • kdmnk
  • kamillacziraki
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The pull request contains some changes that appear tangential to the certificate generation feature: a typo correction in Console/Commands.php and modifications to database configuration (strict mode and Redis prefix) appear unrelated to issue #782 objectives. Consider removing or separating the typo fix and database configuration changes into a distinct pull request to maintain focus on certificate generation functionality.
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Certificates for community service' is directly related to the main objective of this pull request: implementing certificate generation functionality for approved community service requests.
Description check ✅ Passed The pull request description covers the main features (Download button, optional date field, secretariat support), relates to issue #782, and includes supporting materials (screenshots and example PDF). However, it does not follow the suggested template structure with explicit 'Changes' section.
Linked Issues check ✅ Passed The pull request successfully implements both primary objectives from issue #782: submissions can now be made to the secretariat, and approved requests display a Download button to generate PDF certificates with personal data and activity details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch certs-for-service

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (8)
app/Models/CommunityService.php (1)

39-39: date_of_service correctly added to $fillable.

One small housekeeping note: the PHPDoc block at the top of the class is missing a corresponding @property string|null $date_of_service annotation, which would keep the IDE and static analysis tooling in sync.

📝 Suggested docblock addition
  * `@property` string $description
+ * `@property` string|null $date_of_service
  * `@property` int $semester_id
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Models/CommunityService.php` at line 39, Add a PHPDoc property annotation
for the new date_of_service attribute in the CommunityService class docblock:
update the top-of-class PHPDoc to include "@property string|null
$date_of_service" so IDEs and static analyzers recognize the field alongside the
existing $fillable entries and other `@property` annotations in CommunityService.
app/Utils/LatexHelper.php (3)

36-36: Use self:: rather than LatexHelper:: for the internal static call — this also resolves the PHPMD false positive.

Calling a private static method via the class name (LatexHelper::convertCharToSymbol) instead of self:: is what causes PHPMD to report it as "unused". Using self:: is also the idiomatic convention for intra-class static calls in PHP.

♻️ Proposed change
-            $result[] = LatexHelper::convertCharToSymbol(mb_substr($data, $i, 1));
+            $result[] = self::convertCharToSymbol(mb_substr($data, $i, 1));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` at line 36, Replace the internal static call to
LatexHelper::convertCharToSymbol with a self:: call to avoid the PHPMD "unused"
false positive and follow PHP intra-class conventions; locate the call site
where LatexHelper::convertCharToSymbol(...) is invoked (inside the same class,
where mb_substr($data, $i, 1) is passed) and change it to
self::convertCharToSymbol(...), leaving the target private static method
convertCharToSymbol unchanged.

30-30: Loose null comparison == null for a typed parameter.

"" == null evaluates to true in PHP, so sanitizeLatex("") returns "" via the early-return path rather than the (identical) loop path. The result is the same, but using === null is more precise for a string|null parameter and avoids relying on PHP's loose-comparison rules.

♻️ Proposed change
-        if ($data == null) {
+        if ($data === null) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` at line 30, Change the loose null comparison in
the sanitizeLatex method to a strict check: replace the `if ($data == null)`
early-return with a strict `=== null` check because the parameter is typed as
`string|null` and `"" == null` can be true; update the conditional in the
sanitizeLatex function (and any similar checks in class LatexHelper) to use `===
null` to precisely detect null values.

54-54: Filename collision risk — rand(0, 100000) provides only ~100k distinct values.

Under concurrent load, two simultaneous certificate requests can produce the same md5(rand(0, 100000) . date('c')) value, causing one process to overwrite the other's .tex/.pdf file mid-compilation. Str::uuid() or uniqid('', true) provide collision-free identifiers with no extra cost.

♻️ Proposed fix
+        use Illuminate\Support\Str;
         // ...
-        $filename =  md5(rand(0, 100000) . date('c'));
+        $filename = Str::uuid()->toString();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` at line 54, The current $filename generation in
LatexHelper uses md5(rand(0, 100000) . date('c')) which risks collisions;
replace that with a collision-resistant identifier (e.g., Str::uuid() or
uniqid('', true)) when setting $filename in the LatexHelper method that
writes/compiles .tex/.pdf files; if using Str::uuid(), import
Illuminate\Support\Str at the top of the file and assign $filename to the UUID
string, otherwise use uniqid('', true) to ensure unique filenames under
concurrent load.
app/Policies/CommunityServicePolicy.php (1)

63-66: generateCertificate — missing docblock, loose comparison, and absent return type.

Three minor points worth tidying up:

  1. Loose comparison: 1 == $communityService->approved works coincidentally because true == 1 is true in PHP, but the field is already cast to boolean by the model; === true is both clearer and more defensive.
  2. Missing return type: every other method in this policy has a declared return type.
  3. Missing docblock: all other policy methods carry a @param / description block.
♻️ Proposed tidy-up
+    /**
+     * Determine whether the user can generate a certificate for the given community service
+     * (the service must be approved and the user must be its designated approver).
+     * `@param` User $user
+     * `@param` CommunityService $communityService
+     * `@return` bool
+     */
     public function generateCertificate(User $user, CommunityService $communityService)
     {
-        return 1 == $communityService->approved && $communityService->approver->id === $user->id;
+        return $communityService->approved === true && $communityService->approver->id === $user->id;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Policies/CommunityServicePolicy.php` around lines 63 - 66, Update the
generateCertificate method to match the other policy methods: add a docblock
describing parameters (`@param` User $user, `@param` CommunityService
$communityService) and purpose, change the approval check from loose equality to
a strict boolean check (use $communityService->approved === true), ensure the
approver check remains ($communityService->approver->id === $user->id), and
declare a bool return type on the method signature (public function
generateCertificate(User $user, CommunityService $communityService): bool).
app/Models/Role.php (1)

282-288: LGTM — consistent with existing role accessor pattern.

The new secretary() method is a faithful mirror of director(), collegist(), et al. One minor observation: like its siblings, the declared return type Role doesn't account for the null that first() can return when the role doesn't exist in the database. Whilst this is a pre-existing pattern across all similar accessors in this file, it would be worth addressing holistically at some point.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Models/Role.php` around lines 282 - 288, The accessor methods like
secretary() currently declare a non-null return type Role but call first() which
may return null; update secretary() to allow nullable returns (use ?Role or
Role|null) and apply the same change to the sibling accessor methods such as
director() and collegist() so the signatures match the possible null result from
first(); keep the method bodies unchanged.
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php (1)

116-147: The downloaded file lacks a meaningful filename.

response()->download($documentPath) will serve the file with the random hash-based filename generated by LatexHelper. It would be rather more polished to provide a human-readable filename as the second argument — e.g. including the requester's name and the word "certificate".

✏️ Suggested improvement
-        return response()->download($documentPath);
+        $downloadName = 'community_service_certificate_' . $requester->id . '.pdf';
+        return response()->download($documentPath, $downloadName);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 116 - 147, The downloaded PDF uses the temp path from
LatexHelper::generatePDF so response()->download($documentPath) gives a random
filename; update generateCertificate to build a meaningful filename (e.g.
combine $requester->name, "certificate" and the date, sanitize/remove
spaces/special chars and append the .pdf extension) and pass it as the second
argument to response()->download($documentPath, $filename) so the browser
prompts with the human-readable name; ensure you derive the extension from
$documentPath or force ".pdf" and provide a safe fallback filename if
$requester->name is missing.
resources/views/latex/community-service.blade.php (1)

86-89: Hardcoded contact email addresses in the footer.

Lines 86–87 contain literal email addresses (titkarsag@eotvos.elte.hu, horvathl@eotvos.elte.hu, elnok@eotvos.elte.hu). The status-cert.blade.php template (line 56) at least dynamically resolves the director's email via \App\Models\User::director()?->email. This is a pre-existing pattern elsewhere, so it is not strictly new to this PR, but worth noting for future alignment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/latex/community-service.blade.php` around lines 86 - 89, The
footer currently contains hardcoded emails in community-service.blade.php;
replace them with dynamic lookups like the pattern used in status-cert.blade.php
(e.g. \App\Models\User::director()?->email) so addresses come from the User
model or a single source of truth; locate the email string in
community-service.blade.php and substitute calls to the appropriate User
accessor or a role-based query (e.g. director/secretary) or configuration value
so the template renders the current users' emails instead of literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 136-145: The PDF data array accesses
$requester->educationalInformation->neptun without a null check; update the code
that builds the array (the block setting 'requester_neptun') to guard against
missing educational information by checking $requester->educationalInformation
or using $requester->hasEducationalInformation() and provide a safe fallback
(e.g., empty string) for requester_neptun and any other educational fields
before calling the PDF generation in CommunityServiceController (the code
building the array with 'requester_name', 'requester_neptun', etc.).
- Around line 125-132: The code accesses $approvingRole->name without guarding
for a null return from approvingRole(), causing a potential null dereference;
update the logic in the CommunityServiceController method that uses
approvingRole() so it first checks whether $approvingRole is non-null before
comparing to Role::SECRETARY (e.g., if ($approvingRole && $approvingRole->name
=== Role::SECRETARY) { ... }) and only then call User::director() and
Role::director() to swap $approver/$approvingRole; ensure any subsequent use of
$approvingRole assumes a non-null value or is similarly guarded.

In `@app/Utils/LatexHelper.php`:
- Around line 54-69: generatePDF is leaving temporary .tex and .pdf files in the
'latex' storage; remove them after use to avoid disk bloat. After calling
Commands::latexToPdf($pathTex, $outputDir) and after the caller has consumed the
file (i.e., after selecting return value in the generatePDF flow), delete both
$filename . '.tex' and $filename . '.pdf' via
Storage::disk('latex')->delete(...) (or schedule deletion for the debug branch),
or alternatively register a background/shutdown task to unlink
$pathTex/$pathPdf; reference Commands::latexToPdf, $pathTex, $pathPdf and the
debug branch (config('app.debug') && !config('commands.run_in_debug')) when
adding the cleanup logic.
- Around line 50-70: The generatePDF method currently ignores the result of
Commands::latexToPdf and always returns $pathPdf even if compilation failed;
update generatePDF to capture the return value from
Commands::latexToPdf($pathTex, $outputDir), verify it indicates success and/or
that the PDF file at $pathPdf actually exists, and if compilation failed throw
or return a descriptive error (including the captured command output or
exception) or fall back to returning $pathTex in debug mode; ensure you
reference the generatePDF function, the Commands::latexToPdf call, and the
$pathPdf/$pathTex variables when adding the check and error handling.
- Around line 15-38: convertCharToSymbol and sanitizeLatex currently map every
character to \symbol{mb_ord($char)}, but mb_ord returns Unicode code points >255
(e.g., ő → 337) which breaks pdfLaTeX; fix by switching to a Unicode-capable
engine or by replacing the \symbol(...) strategy with proper LaTeX-safe output:
either update the build/CI to use lualatex or xelatex (so UTF-8 characters can
be passed through unchanged) or implement a mapping in convertCharToSymbol that
converts known accented characters to LaTeX accent commands (e.g., ő -> \H{o}, ű
-> \H{u}) and fall back to ascii or numeric-escaped forms for others instead of
\symbol with mb_ord. Ensure changes are applied in convertCharToSymbol and
sanitizeLatex so sanitizeLatex no longer emits invalid \symbol{N} values.

In `@resources/views/latex/community-service.blade.php`:
- Around line 73-74: Replace the hardcoded director name "Dr. Horváth László" in
community-service.blade.php with the same dynamic lookup used elsewhere: call
\App\Models\User::director()?->name (and preserve any title formatting e.g.
"Dr." if needed) so the template displays the current director like
status-cert.blade.php does; update the line that currently contains the literal
name to use that dynamic expression and ensure it still falls back gracefully if
null.
- Line 3: Replace the fragile relative LaTeX resource path defined by the
\resourcepath command with the same absolute Laravel storage helper used in
status-cert.blade.php: remove the \newcommand{\resourcepath}{../public/} line
and update image/include references in community-service.blade.php to use
storage_path('/app/public/<filename>') (e.g.
storage_path('/app/public/epulet.png')) so paths are absolute and consistent
with status-cert.blade.php.

In `@resources/views/student-council/community-service/request.blade.php`:
- Line 14: User::secretary() can return null which will be injected into the
elements array passed to the x-input.select component causing a fatal render
error; update the elements construction so any null/falsey values are filtered
out before merging (e.g., filter the array containing User::secretary() and the
result of User::studentCouncilLeaders()) so x-input.select only receives valid
User objects; reference User::secretary(), User::studentCouncilLeaders(), and
the x-input.select id="approver" elements prop when making the change.

---

Nitpick comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 116-147: The downloaded PDF uses the temp path from
LatexHelper::generatePDF so response()->download($documentPath) gives a random
filename; update generateCertificate to build a meaningful filename (e.g.
combine $requester->name, "certificate" and the date, sanitize/remove
spaces/special chars and append the .pdf extension) and pass it as the second
argument to response()->download($documentPath, $filename) so the browser
prompts with the human-readable name; ensure you derive the extension from
$documentPath or force ".pdf" and provide a safe fallback filename if
$requester->name is missing.

In `@app/Models/CommunityService.php`:
- Line 39: Add a PHPDoc property annotation for the new date_of_service
attribute in the CommunityService class docblock: update the top-of-class PHPDoc
to include "@property string|null $date_of_service" so IDEs and static analyzers
recognize the field alongside the existing $fillable entries and other `@property`
annotations in CommunityService.

In `@app/Models/Role.php`:
- Around line 282-288: The accessor methods like secretary() currently declare a
non-null return type Role but call first() which may return null; update
secretary() to allow nullable returns (use ?Role or Role|null) and apply the
same change to the sibling accessor methods such as director() and collegist()
so the signatures match the possible null result from first(); keep the method
bodies unchanged.

In `@app/Policies/CommunityServicePolicy.php`:
- Around line 63-66: Update the generateCertificate method to match the other
policy methods: add a docblock describing parameters (`@param` User $user, `@param`
CommunityService $communityService) and purpose, change the approval check from
loose equality to a strict boolean check (use $communityService->approved ===
true), ensure the approver check remains ($communityService->approver->id ===
$user->id), and declare a bool return type on the method signature (public
function generateCertificate(User $user, CommunityService $communityService):
bool).

In `@app/Utils/LatexHelper.php`:
- Line 36: Replace the internal static call to LatexHelper::convertCharToSymbol
with a self:: call to avoid the PHPMD "unused" false positive and follow PHP
intra-class conventions; locate the call site where
LatexHelper::convertCharToSymbol(...) is invoked (inside the same class, where
mb_substr($data, $i, 1) is passed) and change it to
self::convertCharToSymbol(...), leaving the target private static method
convertCharToSymbol unchanged.
- Line 30: Change the loose null comparison in the sanitizeLatex method to a
strict check: replace the `if ($data == null)` early-return with a strict `===
null` check because the parameter is typed as `string|null` and `"" == null` can
be true; update the conditional in the sanitizeLatex function (and any similar
checks in class LatexHelper) to use `=== null` to precisely detect null values.
- Line 54: The current $filename generation in LatexHelper uses md5(rand(0,
100000) . date('c')) which risks collisions; replace that with a
collision-resistant identifier (e.g., Str::uuid() or uniqid('', true)) when
setting $filename in the LatexHelper method that writes/compiles .tex/.pdf
files; if using Str::uuid(), import Illuminate\Support\Str at the top of the
file and assign $filename to the UUID string, otherwise use uniqid('', true) to
ensure unique filenames under concurrent load.

In `@resources/views/latex/community-service.blade.php`:
- Around line 86-89: The footer currently contains hardcoded emails in
community-service.blade.php; replace them with dynamic lookups like the pattern
used in status-cert.blade.php (e.g. \App\Models\User::director()?->email) so
addresses come from the User model or a single source of truth; locate the email
string in community-service.blade.php and substitute calls to the appropriate
User accessor or a role-based query (e.g. director/secretary) or configuration
value so the template renders the current users' emails instead of literals.

Comment thread app/Http/Controllers/StudentsCouncil/CommunityServiceController.php Outdated
Comment on lines +136 to +145
[
'requester_name' => $requester->name,
'requester_neptun' => $requester->educationalInformation->neptun,
'approver_name' => $approver->name,
'approver_title' => is_null($approvingRole) ? "" : $approvingRole->translatedName,
'service_description' => $communityService->description,
'date_of_service' => $communityService->date_of_service,
'current_date' => date("Y.m.d.")
]
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential null pointer dereference on educationalInformation.

Line 138 accesses $requester->educationalInformation->neptun without verifying that educationalInformation is not null. If the requester lacks educational information, this will throw a fatal error. The DocumentController::generateStatusCertificate method (line 285–290 in DocumentController.php) guards against this with hasEducationalInformation() — this method would benefit from a similar check.

🛡️ Proposed fix — add a guard before PDF generation
         $requester = $communityService->requester;
+        if (!$requester->hasEducationalInformation()) {
+            return back()->with('error', 'A kérvényező tanulmányi adatai hiányoznak.');
+        }
         $approver = Auth::user();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 136 - 145, The PDF data array accesses
$requester->educationalInformation->neptun without a null check; update the code
that builds the array (the block setting 'requester_neptun') to guard against
missing educational information by checking $requester->educationalInformation
or using $requester->hasEducationalInformation() and provide a safe fallback
(e.g., empty string) for requester_neptun and any other educational fields
before calling the PDF generation in CommunityServiceController (the code
building the array with 'requester_name', 'requester_neptun', etc.).

Comment thread app/Utils/LatexHelper.php
Comment on lines +15 to +38
private static function convertCharToSymbol(string $char): string
{
if ($char == ' ') {
return ' ';
}
return "\\symbol{" . mb_ord($char) . "}";
}

/**
* Converts string to relatively safe LaTeX code by putting every character into a seperate \symbol{...}
* @param string $data
* @return string
*/
public static function sanitizeLatex(string|null $data): string
{
if ($data == null) {
return "";
}
$len = mb_strlen($data);
$result = [];
for ($i = 0; $i < $len; $i++) {
$result[] = LatexHelper::convertCharToSymbol(mb_substr($data, $i, 1));
}
return implode($result);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the LatexHelper.php file
fd 'LatexHelper.php' --type f

Repository: EotvosCollegium/mars

Length of output: 89


🏁 Script executed:

# Search for LaTeX engine configuration in the codebase
rg -n 'pdflatex|lualatex|xelatex' --type=php

Repository: EotvosCollegium/mars

Length of output: 302


🏁 Script executed:

# Search for the Commands class and latexToPdf method
rg -n 'latexToPdf|class Commands' --type=php

Repository: EotvosCollegium/mars

Length of output: 272


🏁 Script executed:

# Look for certificate template files
fd 'community-service|certificate' --type f | grep -E '\.(blade\.php|php)$'

Repository: EotvosCollegium/mars

Length of output: 210


🏁 Script executed:

# Search for \symbol usage in templates and views
rg -n '\\\\symbol' --type=php

Repository: EotvosCollegium/mars

Length of output: 142


🏁 Script executed:

# Read the full LatexHelper.php file
cat -n app/Utils/LatexHelper.php

Repository: EotvosCollegium/mars

Length of output: 2569


🏁 Script executed:

# Read the Commands class with focus on latexToPdf
cat -n app/Console/Commands.php

Repository: EotvosCollegium/mars

Length of output: 1327


🏁 Script executed:

# Read the certificate template
cat -n resources/views/latex/community-service.blade.php

Repository: EotvosCollegium/mars

Length of output: 4685


🌐 Web query:

\symbol{} pdflatex valid range 0-255 character code

💡 Result:

In pdfLaTeX (pdfTeX), \symbol{<number>} accesses a glyph by 8‑bit font position, so the valid character code range is 0–255; if you try a larger value (e.g., a Unicode code point like U+20AE = 8366) you’ll get the classic error “character number must be between 0 and 255.” (tug.ctan.org)

If you need code points beyond 255, use a Unicode engine (XeLaTeX/LuaLaTeX), where you can input Unicode scalar values (e.g. \symbol{"3042} / \char"3042). (tex.stackexchange.com)

Citations:


🌐 Web query:

Hungarian character ő unicode code point

💡 Result:

The Hungarian letter ő (Latin small letter o with double acute) has Unicode code point U+0151 (decimal 337). [1][2]


🌐 Web query:

pdflatex \symbol font encoding T1 valid codes

💡 Result:

In pdfLaTeX (8‑bit fonts), \symbol{<n>} (like \char) accepts a character code in the range 0–255; larger values are invalid (“character number must be between 0 and 255”). [1]

With font encoding T1 active (\usepackage[T1]{fontenc}), those numbers index the 256 slots of the Cork/T1 encoding (i.e., the glyph position in the currently selected T1-encoded font). [2]

Key points for “valid codes” in T1:

  • Syntactically valid: any integer 0–255 (decimal), also expressible in octal/hex TeX notation. [1]
  • Semantically valid (produces the glyph you expect): only the codes that your specific T1 font actually contains for that slot; T1 is a mix and is not meant to be typed as raw codes. [3]
  • The authoritative mapping from LaTeX accent/letter commands to T1 slots is in t1enc.def (e.g., it maps \'a to slot 225 in T1). [4]

If what you want is “which number gives which character in T1”, use the Cork/T1 encoding table (and/or inspect t1enc.def), because \symbol just addresses slots—it doesn’t define the table by itself. [2][4]

Sources: [1] Google Groups thread showing TeX’s 0–255 limit for \char-style primitives https://groups.google.com/g/linux.debian.user/c/VymnFqV6B3Y, [2] Cork/T1 is a 256-character encoding https://en.wikipedia.org/wiki/Cork_encoding, [3] TeX FAQ note that T1 code points are a non-keyboard mix and fontenc generates them from LaTeX commands https://davidcarlisle.github.io/uk-tex-faq/FAQ-t1enc.html, [4] Example from t1enc.def mapping \'a → 225 (T1 slot) https://tex.stackexchange.com/questions/276145/what-does-fontenc-do-and-when-is-it-applied-based-on-an-example-of-creating-my-package


\symbol{N} with pdfLatex fails fatally for Hungarian characters — mb_ord() returns Unicode code points exceeding the 0–255 limit.

\symbol{N} in pdfLatex addresses font glyphs by 8-bit slot number (0–255). The mb_ord() function, however, returns a Unicode scalar value. For the Hungarian character ő, this is U+0151 (decimal 337), which exceeds 255 and triggers a fatal compilation error: "character number must be between 0 and 255."

The template calls LatexHelper::sanitizeLatex() on all user input—names, descriptions, and dates—and the certificate template is configured for Hungarian language (\usepackage[magyar]{babel}). Any user input containing ő, ű, or other accented characters with code points exceeding 255 will cause compilation failure in production.

The sustainable remedy is to migrate from pdflatex to a Unicode-aware engine such as lualatex or xelatex, which handle UTF-8 encoded Unicode natively. Alternatively, implement proper LaTeX accent commands (e.g., ő\H{o}, ű\H{u}) in place of the current \symbol{} approach.

🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 15-21: Avoid unused private methods such as 'convertCharToSymbol'. (undefined)

(UnusedPrivateMethod)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` around lines 15 - 38, convertCharToSymbol and
sanitizeLatex currently map every character to \symbol{mb_ord($char)}, but
mb_ord returns Unicode code points >255 (e.g., ő → 337) which breaks pdfLaTeX;
fix by switching to a Unicode-capable engine or by replacing the \symbol(...)
strategy with proper LaTeX-safe output: either update the build/CI to use
lualatex or xelatex (so UTF-8 characters can be passed through unchanged) or
implement a mapping in convertCharToSymbol that converts known accented
characters to LaTeX accent commands (e.g., ő -> \H{o}, ű -> \H{u}) and fall back
to ascii or numeric-escaped forms for others instead of \symbol with mb_ord.
Ensure changes are applied in convertCharToSymbol and sanitizeLatex so
sanitizeLatex no longer emits invalid \symbol{N} values.

Comment thread app/Utils/LatexHelper.php
Comment on lines +50 to +70
public static function generatePDF($path, $data)
{
$renderedLatex = view($path)->with($data)->render();

$filename = md5(rand(0, 100000) . date('c'));
Storage::disk('latex')->put($filename . '.tex', $renderedLatex);

$outputDir = Storage::disk('latex')->path('/');

$pathTex = Storage::disk('latex')->path($filename . ".tex");
$pathPdf = Storage::disk('latex')->path($filename . ".pdf");

// TODO: figure out result
Commands::latexToPdf($pathTex, $outputDir);

if (config('app.debug') && !config('commands.run_in_debug')) {
return $pathTex;
} else {
return $pathPdf;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

latexToPdf return value is silently discarded — compilation failures are invisible, and a non-existent PDF path is returned.

Line 63 calls Commands::latexToPdf(...) but never assigns or checks the result. The TODO: figure out result comment confirms this is known but unresolved. If pdflatex fails to compile (syntax error, missing package, or the \symbol overflow described above), the .pdf file will not be created, yet $pathPdf is returned unconditionally on line 68. The caller will then attempt to serve a non-existent file, resulting in a 500 error or a silent broken download.

🛡️ Proposed error-handling skeleton
-        // TODO: figure out result
-        Commands::latexToPdf($pathTex, $outputDir);
-
-        if (config('app.debug') && !config('commands.run_in_debug')) {
-            return $pathTex;
-        } else {
-            return $pathPdf;
-        }
+        Commands::latexToPdf($pathTex, $outputDir);
+
+        if (config('app.debug') && !config('commands.run_in_debug')) {
+            return $pathTex;
+        }
+
+        if (!file_exists($pathPdf)) {
+            throw new \RuntimeException('LaTeX compilation failed; PDF was not produced. Check the .tex file at: ' . $pathTex);
+        }
+
+        return $pathPdf;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` around lines 50 - 70, The generatePDF method
currently ignores the result of Commands::latexToPdf and always returns $pathPdf
even if compilation failed; update generatePDF to capture the return value from
Commands::latexToPdf($pathTex, $outputDir), verify it indicates success and/or
that the PDF file at $pathPdf actually exists, and if compilation failed throw
or return a descriptive error (including the captured command output or
exception) or fall back to returning $pathTex in debug mode; ensure you
reference the generatePDF function, the Commands::latexToPdf call, and the
$pathPdf/$pathTex variables when adding the check and error handling.

Comment thread app/Utils/LatexHelper.php
Comment on lines +54 to +69
$filename = md5(rand(0, 100000) . date('c'));
Storage::disk('latex')->put($filename . '.tex', $renderedLatex);

$outputDir = Storage::disk('latex')->path('/');

$pathTex = Storage::disk('latex')->path($filename . ".tex");
$pathPdf = Storage::disk('latex')->path($filename . ".pdf");

// TODO: figure out result
Commands::latexToPdf($pathTex, $outputDir);

if (config('app.debug') && !config('commands.run_in_debug')) {
return $pathTex;
} else {
return $pathPdf;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Temporary .tex and .pdf files are never cleaned up — disk space will accumulate indefinitely.

Every call to generatePDF creates two files in the latex storage disk and leaves them there permanently. On a production server with regular certificate generation activity, this will gradually exhaust available disk space with no automated remedy.

Consider deleting the files after the caller has consumed them (e.g., after the HTTP response is sent), or scheduling a periodic clean-up of old files in the storage directory.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` around lines 54 - 69, generatePDF is leaving
temporary .tex and .pdf files in the 'latex' storage; remove them after use to
avoid disk bloat. After calling Commands::latexToPdf($pathTex, $outputDir) and
after the caller has consumed the file (i.e., after selecting return value in
the generatePDF flow), delete both $filename . '.tex' and $filename . '.pdf' via
Storage::disk('latex')->delete(...) (or schedule deletion for the debug branch),
or alternatively register a background/shutdown task to unlink
$pathTex/$pathPdf; reference Commands::latexToPdf, $pathTex, $pathPdf and the
debug branch (config('app.debug') && !config('commands.run_in_debug')) when
adding the cleanup logic.

@@ -0,0 +1,132 @@
\documentclass[11pt,a4paper]{article}

\newcommand{\resourcepath}{../public/}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the blade template files mentioned in the review
find . -name "community-service.blade.php" -o -name "status-cert.blade.php" 2>/dev/null | head -20

Repository: EotvosCollegium/mars

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Search for LatexHelper to understand working directory configuration
find . -name "*LatexHelper*" -o -name "*latex*helper*" 2>/dev/null | grep -i helper | head -20

Repository: EotvosCollegium/mars

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Get a broader view of the project structure
ls -la | head -20

Repository: EotvosCollegium/mars

Length of output: 1276


🏁 Script executed:

cat -n ./resources/views/latex/community-service.blade.php

Repository: EotvosCollegium/mars

Length of output: 4685


🏁 Script executed:

cat -n ./resources/views/latex/status-cert.blade.php

Repository: EotvosCollegium/mars

Length of output: 4278


🏁 Script executed:

cat -n ./app/Utils/LatexHelper.php

Repository: EotvosCollegium/mars

Length of output: 2569


🏁 Script executed:

find . -name "epulet.png" -o -name "szabadon_szolgal_a_szellem.png" 2>/dev/null

Repository: EotvosCollegium/mars

Length of output: 95


🏁 Script executed:

rg "epulet.png|szabadon_szolgal_a_szellem.png" -g "*.php" -g "*.blade.php" --max-count 10

Repository: EotvosCollegium/mars

Length of output: 581


Resource path must use absolute Laravel helpers, consistent with status-cert.blade.php.

The relative resource path ../public/ is fragile, depending on LaTeX compilation working directory configuration. Moreover, it produces double slashes in resolved paths (e.g. ../public//epulet.png).

Whilst status-cert.blade.php (line 24) correctly uses an absolute path with storage_path('/app/public/epulet.png'), community-service.blade.php relies on a relative path. For consistency and robustness, adopt the same approach using storage_path():

Suggested fix
-\newcommand{\resourcepath}{../public/}
...
-\node[inner sep=0pt] (picture) at (0,2){\includegraphics[width=5cm]{\resourcepath/epulet.png}};
+\node[inner sep=0pt] (picture) at (0,2){\includegraphics[width=5cm]{ {{ storage_path('/app/public/epulet.png') }} }};
...
-\node[inner sep=0pt] (from) at (0,-0.4){\includegraphics[width=5cm]{\resourcepath/szabadon_szolgal_a_szellem.png}};
+\node[inner sep=0pt] (from) at (0,-0.4){\includegraphics[width=5cm]{ {{ storage_path('/app/public/szabadon_szolgal_a_szellem.png') }} }};

Remove the \resourcepath command entirely once resolved.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/latex/community-service.blade.php` at line 3, Replace the
fragile relative LaTeX resource path defined by the \resourcepath command with
the same absolute Laravel storage helper used in status-cert.blade.php: remove
the \newcommand{\resourcepath}{../public/} line and update image/include
references in community-service.blade.php to use
storage_path('/app/public/<filename>') (e.g.
storage_path('/app/public/epulet.png')) so paths are absolute and consistent
with status-cert.blade.php.

Comment on lines +73 to +74
Dr. Horváth László\\
igazgató\\
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded director name will become stale.

The footer contains Dr. Horváth László as a literal string, whereas the status-cert.blade.php template (line 45, 79, 98) dynamically resolves the director's name via \App\Models\User::director()?->name. Should the director change, this template will silently display the wrong name.

One would respectfully recommend using the same dynamic approach here:

🐛 Proposed fix
-		    Dr. Horváth László\\
+		    {{ \App\Utils\LatexHelper::sanitizeLatex(\App\Models\User::director()?->name) }}\\
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/latex/community-service.blade.php` around lines 73 - 74,
Replace the hardcoded director name "Dr. Horváth László" in
community-service.blade.php with the same dynamic lookup used elsewhere: call
\App\Models\User::director()?->name (and preserve any title formatting e.g.
"Dr." if needed) so the template displays the current director like
status-cert.blade.php does; update the line that currently contains the literal
name to use that dynamic expression and ensure it still falls back gracefully if
null.

<x-input.button s=2 class="right" text="Mentés" />
<x-input.text m=12 id="description" required text="Rendezvény, feladatkör, tevékenység" />
<x-input.text m=6 id="date_of_service" text="Tevékenység dátuma" />
<x-input.select m=4 id="approver" :elements="array_merge([\App\Models\User::secretary()], \App\Models\User::studentCouncilLeaders()->toArray())" text="jóváhagyó"/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

User::secretary() can return null, which will be injected into the elements array and likely cause a fatal render error.

User::secretary() is declared as ?User and returns null when no secretary is currently assigned. Passing that null as the first element of the array to x-input.select means the component will attempt to access properties on null, causing an unhandled error for any user who visits the form while the role is vacant.

🛡️ Proposed fix — filter out null before merging
-<x-input.select m=4 id="approver" :elements="array_merge([\App\Models\User::secretary()], \App\Models\User::studentCouncilLeaders()->toArray())" text="jóváhagyó"/>
+<x-input.select m=4 id="approver" :elements="array_merge(array_filter([\App\Models\User::secretary()]), \App\Models\User::studentCouncilLeaders()->toArray())" text="jóváhagyó"/>

array_filter with no callback removes null (and other falsy values), so a vacant secretary slot is simply omitted from the list rather than causing an error.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<x-input.select m=4 id="approver" :elements="array_merge([\App\Models\User::secretary()], \App\Models\User::studentCouncilLeaders()->toArray())" text="jóváhagyó"/>
<x-input.select m=4 id="approver" :elements="array_merge(array_filter([\App\Models\User::secretary()]), \App\Models\User::studentCouncilLeaders()->toArray())" text="jóváhagyó"/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/student-council/community-service/request.blade.php` at line
14, User::secretary() can return null which will be injected into the elements
array passed to the x-input.select component causing a fatal render error;
update the elements construction so any null/falsey values are filtered out
before merging (e.g., filter the array containing User::secretary() and the
result of User::studentCouncilLeaders()) so x-input.select only receives valid
User objects; reference User::secretary(), User::studentCouncilLeaders(), and
the x-input.select id="approver" elements prop when making the change.

@horcsinbalint
Copy link
Copy Markdown
Member

Anitával átbeszéltétek a funkcionális követelményeket? Én ugyan nem tárgyaltam vele végig részletesen a kérdéseket, de valami olyasmi rémlik, hogy olyan módon kéri Anita a különböző dolgokat, hogy az adott esemény adatait ki kelljen az illetőnek töltenie, különböző fieldekben a rendezvény kitöltendő adatai. Illetve emlékeim szerint előadások tartására is ezt szeretnék, hogyha használva lenne, ami nem hiszem, hogy közösségi tevékenységnek számít.

@viktorcsimma
Copy link
Copy Markdown
Contributor Author

viktorcsimma commented Mar 8, 2026

@horcsinbalint Igen, küldött pár mintát és annak ez megfelel:) de megkérdezhetem tőle még egyszer. A közösségi tevékenységből áttehetjük akár külön menüpontba; én inkább csak azért vontam össze, mert így tudom azt az infrastruktúrát használni és nem kell a kereket újrafeltalálni.

P.S. bocsi, hogy csak most; nem küldött értesítést és nem vettem észre 😅

viktorcsimma and others added 6 commits March 11, 2026 11:50
This commit fixes the style issues introduced in ca53732 according to the output
from PHP CS Fixer.

Details: #791
This commit fixes the style issues introduced in 15e5395 according to the output
from PHP CS Fixer.

Details: #791
@viktorcsimma
Copy link
Copy Markdown
Contributor Author

viktorcsimma commented Mar 11, 2026

Now, I've moved the secretariat's part under the Documents page, so that it's clearly separated from community services. See the updated intro (it's terribly littered by AI comments 😅).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php (1)

53-66: ⚠️ Potential issue | 🔴 Critical

Do not trust the approver from the form.

approver is only checked for existence, so a crafted POST can nominate any user ID — including the requester themselves. Because both approve() and generateCertificate() only verify approver_id === user->id, that lets a user self-approve and then download their own certificate. Please validate the approver against a server-side allow-list of valid secretariat/student-council approvers and reject self-selection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 53 - 66, The form currently trusts the submitted 'approver' and creates a
CommunityService via CommunityService::create using $request->approver; instead,
before creation validate that the chosen approver is in your server-side
allow-list of authorized approvers (e.g. users with the
student-council/secretariat role) and reject the request if not, and also reject
if $request->approver == user()->id to prevent self-selection; update the
controller code around the request->validate and CommunityService::create blocks
to lookup the approver via the Users/Role query (or an AllowList service),
enforce membership in that allowed set, and only then set 'approver_id' for the
new CommunityService (also ensure approve() and generateCertificate() continue
to rely on approver_id === user()->id and will thus be safe once approver_id is
server-validated).
♻️ Duplicate comments (3)
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php (2)

123-132: ⚠️ Potential issue | 🔴 Critical

Guard approvingRole() before dereferencing it.

approvingRole() explicitly returns null, and that can still happen later if an already-approved approver loses the relevant role. Line 125 will then fatal before the director fallback is reached.

Possible fix
-        if (Role::SECRETARY == $approvingRole->name) {
+        if ($approvingRole && $approvingRole->name === Role::SECRETARY) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 123 - 132, approvingRole() can return null and the code dereferences
$approvingRole->name causing a fatal; guard the call in
CommunityServiceController by checking for null before accessing ->name (e.g.,
if ($approvingRole !== null && Role::SECRETARY == $approvingRole->name) { ... })
so the director fallback still executes when $approvingRole is null, and ensure
you update $approver and $approvingRole to User::director() and Role::director()
only inside that guarded block.

121-139: ⚠️ Potential issue | 🟠 Major

Fail gracefully when the requester has no educational record.

Line 138 assumes educationalInformation is present, but that relation is nullable elsewhere and DocumentController::generateStatusCertificate() already guards it. At present this path returns a 500 instead of a user-facing error.

Possible fix
         $requester = $communityService->requester;
+        if (!$requester->hasEducationalInformation()) {
+            return back()->with('error', 'A kérvényező tanulmányi adatai hiányoznak az igazolás generálásához.');
+        }
         $approver = Auth::user();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 121 - 139, The code in CommunityServiceController reads
$requester->educationalInformation->neptun inside the generatePDF call which can
be null and causes a 500; update the method to check that
$requester->educationalInformation exists before using ->neptun (e.g. if null,
return a user-facing error/redirect or throw a handled exception), and pass a
safe value to \App\Utils\LatexHelper::generatePDF only when present; reference
the $requester variable, the educationalInformation relation, and the
generatePDF call to locate and fix the check.
resources/views/student-council/community-service/request.blade.php (1)

1-5: ⚠️ Potential issue | 🟠 Major

Default $isForSecretariat and filter out an absent secretary.

This partial is still included from resources/views/secretariat/evaluation-form/community_service.blade.php without that variable, so the new branch now raises an undefined-variable error there. Also, when no secretary is assigned, [User::secretary()] still injects null into the select payload.

Possible fix
 `@php`
+    $isForSecretariat = $isForSecretariat ?? false;
     $approvers = $isForSecretariat
-                ? [\App\Models\User::secretary()]
+                ? array_values(array_filter([\App\Models\User::secretary()]))
                 : \App\Models\User::studentCouncilLeaders()->toArray();
 `@endphp`

Also applies to: 10-21

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/student-council/community-service/request.blade.php` around
lines 1 - 5, The partial uses $isForSecretariat and builds $approvers which can
be undefined or contain nulls; ensure $isForSecretariat has a default (e.g.
false) before the ternary and when building $approvers filter out any null
entries from the result of User::secretary() or User::studentCouncilLeaders() so
the select payload never contains null; update the Blade snippet that sets
$isForSecretariat and $approvers (the variables in the shown `@php` block) to
provide a default for $isForSecretariat and apply a null-filter to the chosen
array/result.
🧹 Nitpick comments (2)
app/Http/Controllers/Secretariat/DocumentController.php (1)

26-32: Avoid loading certificate history on every documents-page hit.

resources/views/secretariat/document/index.blade.php only renders the community-service table for secretaries, but this query now runs for every /documents request. When you do need it, please eager-load communityServices.requester and communityServices.approver as well, otherwise the reused table partial fans out into N+1 queries.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/Secretariat/DocumentController.php` around lines 26 -
32, The Semester query in printingBladeData() is being executed on every
/documents request; change it to run only when the current user will render the
community-service table (e.g., check the user's secretary role/permission before
running the query) and eager-load the nested relations to avoid N+1s by using
with(...) for communityServices.requester and communityServices.approver (or by
eager-loading communityServices with those relations inside the Semester query
that currently uses withWhereHas('communityServices', ...)); keep the existing
approver/requester filter logic but move it behind the role check so the query
only executes when needed.
app/Utils/LatexHelper.php (1)

55-55: Consider using a cryptographically stronger random source for filename generation.

Whilst collision is improbable given that date('c') provides per-second uniqueness, rand(0, 100000) offers rather limited entropy. Under concurrent load within the same second, a collision is theoretically possible.

♻️ Proposed improvement using Laravel's Str::random()
+use Illuminate\Support\Str;
+
 // ...
 
-        $filename =  md5(rand(0, 100000) . date('c'));
+        $filename = Str::random(40);

Alternatively, retain the timestamp for debugging visibility:

-        $filename =  md5(rand(0, 100000) . date('c'));
+        $filename = date('Ymd_His') . '_' . Str::random(16);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` at line 55, Replace the weak rand(0, 100000)
entropy used to build $filename in LatexHelper.php with a cryptographically
secure random source (e.g. use Laravel's Illuminate\Support\Str::random(...) or
PHP's random_bytes/bin2hex) and keep the timestamp if you want human-readable
uniqueness; update the $filename construction in the method that sets $filename
to combine date('c') with a secure random string and add the appropriate import
(Str) or error handling for random_bytes as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Models/Role.php`:
- Around line 282-288: The methods (secretary(), collegist(), studentsCouncil(),
director(), sysAdmin()) declare a non-nullable Role return but use
self::where(...)->first() which can return null; replace each body to delegate
to self::get(CONSTANT) (e.g., return self::get(self::SECRETARY)) so the lookup
throws/handles missing roles and satisfies the non-nullable Role contract;
update each respective method to call self::get with its constant
(self::COLLEGIST, self::STUDENTS_COUNCIL, self::DIRECTOR, self::SYS_ADMIN)
instead of where(...)->first().

---

Outside diff comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 53-66: The form currently trusts the submitted 'approver' and
creates a CommunityService via CommunityService::create using
$request->approver; instead, before creation validate that the chosen approver
is in your server-side allow-list of authorized approvers (e.g. users with the
student-council/secretariat role) and reject the request if not, and also reject
if $request->approver == user()->id to prevent self-selection; update the
controller code around the request->validate and CommunityService::create blocks
to lookup the approver via the Users/Role query (or an AllowList service),
enforce membership in that allowed set, and only then set 'approver_id' for the
new CommunityService (also ensure approve() and generateCertificate() continue
to rely on approver_id === user()->id and will thus be safe once approver_id is
server-validated).

---

Duplicate comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 123-132: approvingRole() can return null and the code dereferences
$approvingRole->name causing a fatal; guard the call in
CommunityServiceController by checking for null before accessing ->name (e.g.,
if ($approvingRole !== null && Role::SECRETARY == $approvingRole->name) { ... })
so the director fallback still executes when $approvingRole is null, and ensure
you update $approver and $approvingRole to User::director() and Role::director()
only inside that guarded block.
- Around line 121-139: The code in CommunityServiceController reads
$requester->educationalInformation->neptun inside the generatePDF call which can
be null and causes a 500; update the method to check that
$requester->educationalInformation exists before using ->neptun (e.g. if null,
return a user-facing error/redirect or throw a handled exception), and pass a
safe value to \App\Utils\LatexHelper::generatePDF only when present; reference
the $requester variable, the educationalInformation relation, and the
generatePDF call to locate and fix the check.

In `@resources/views/student-council/community-service/request.blade.php`:
- Around line 1-5: The partial uses $isForSecretariat and builds $approvers
which can be undefined or contain nulls; ensure $isForSecretariat has a default
(e.g. false) before the ternary and when building $approvers filter out any null
entries from the result of User::secretary() or User::studentCouncilLeaders() so
the select payload never contains null; update the Blade snippet that sets
$isForSecretariat and $approvers (the variables in the shown `@php` block) to
provide a default for $isForSecretariat and apply a null-filter to the chosen
array/result.

---

Nitpick comments:
In `@app/Http/Controllers/Secretariat/DocumentController.php`:
- Around line 26-32: The Semester query in printingBladeData() is being executed
on every /documents request; change it to run only when the current user will
render the community-service table (e.g., check the user's secretary
role/permission before running the query) and eager-load the nested relations to
avoid N+1s by using with(...) for communityServices.requester and
communityServices.approver (or by eager-loading communityServices with those
relations inside the Semester query that currently uses
withWhereHas('communityServices', ...)); keep the existing approver/requester
filter logic but move it behind the role check so the query only executes when
needed.

In `@app/Utils/LatexHelper.php`:
- Line 55: Replace the weak rand(0, 100000) entropy used to build $filename in
LatexHelper.php with a cryptographically secure random source (e.g. use
Laravel's Illuminate\Support\Str::random(...) or PHP's random_bytes/bin2hex) and
keep the timestamp if you want human-readable uniqueness; update the $filename
construction in the method that sets $filename to combine date('c') with a
secure random string and add the appropriate import (Str) or error handling for
random_bytes as needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6fef1f0a-9689-4bd0-9c2a-0c19a20da01f

📥 Commits

Reviewing files that changed from the base of the PR and between acbfa1a and 107f095.

📒 Files selected for processing (15)
  • .gitignore
  • app/Http/Controllers/Secretariat/DocumentController.php
  • app/Http/Controllers/StudentsCouncil/CommunityServiceController.php
  • app/Models/CommunityService.php
  • app/Models/Role.php
  • app/Policies/CommunityServicePolicy.php
  • app/Utils/LatexHelper.php
  • config/database.php
  • database/migrations/2026_02_13_184249_community_service_add_date_of_service.php
  • resources/views/latex/community-service.blade.php
  • resources/views/secretariat/document/index.blade.php
  • resources/views/student-council/community-service/app.blade.php
  • resources/views/student-council/community-service/request.blade.php
  • resources/views/student-council/community-service/table.blade.php
  • routes/web.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/Models/CommunityService.php
  • app/Policies/CommunityServicePolicy.php
  • resources/views/secretariat/document/index.blade.php
  • resources/views/latex/community-service.blade.php

Comment thread app/Models/Role.php
Comment on lines +282 to +288
/**
* Returns the role for the (director's) secretary.
*/
public static function secretary(): Role
{
return self::where('name', self::SECRETARY)->first();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the structure and find the Role model
find . -type f -name "Role.php" | head -20

Repository: EotvosCollegium/mars

Length of output: 85


🏁 Script executed:

# Read the Role.php file to understand the full context around the secretary() method
cat -n app/Models/Role.php | head -300

Repository: EotvosCollegium/mars

Length of output: 12527


🏁 Script executed:

# Let's also check if there's a get() method in the Role model and understand its return type
rg -A 10 "public static function get\(" app/Models/Role.php

Repository: EotvosCollegium/mars

Length of output: 465


🏁 Script executed:

# Search for usages of the secretary() method to see how it's called
rg "secretary\(\)" . --type php -B 2 -A 2

Repository: EotvosCollegium/mars

Length of output: 3843


Return-type contract must match implementation.

The method declares Role (non-nullable) but first() returns Role|null. This creates a runtime risk: if the secretary role is missing from the database, the method returns null, violating its type contract and potentially causing a TypeError in calling code. Delegate to self::get(self::SECRETARY), which properly handles null cases by throwing an exception and maintains the non-null contract.

Note: The methods collegist(), studentsCouncil(), director(), and sysAdmin() suffer the same issue.

Suggested fix
     public static function secretary(): Role
     {
-        return self::where('name', self::SECRETARY)->first();
+        return self::get(self::SECRETARY);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Models/Role.php` around lines 282 - 288, The methods (secretary(),
collegist(), studentsCouncil(), director(), sysAdmin()) declare a non-nullable
Role return but use self::where(...)->first() which can return null; replace
each body to delegate to self::get(CONSTANT) (e.g., return
self::get(self::SECRETARY)) so the lookup throws/handles missing roles and
satisfies the non-nullable Role contract; update each respective method to call
self::get with its constant (self::COLLEGIST, self::STUDENTS_COUNCIL,
self::DIRECTOR, self::SYS_ADMIN) instead of where(...)->first().

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php (1)

53-66: ⚠️ Potential issue | 🟠 Major

Validate approver against the allowed recipients on the server.

Line 54 only checks users.id, so a crafted POST can target any account even though the form limits choices in resources/views/student-council/community-service/request.blade.php:2-4. Please whitelist the secretary and Student Council leaders here as well.

Suggested fix
+use Illuminate\Validation\Rule;
...
+        $allowedApproverIds = collect([User::secretary()?->id])
+            ->filter()
+            ->merge(collect(User::studentCouncilLeaders())->pluck('id'))
+            ->all();
+
         $request->validate([
-            'approver' => 'required|exists:users,id',
+            'approver' => ['required', 'exists:users,id', Rule::in($allowedApproverIds)],
             'description' => 'required|string',
             'date_of_service' => 'nullable|string',
         ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 53 - 66, The current validation only ensures approver exists in users but
not that they are an allowed recipient; update the validation logic around
request->validate (the approver rule) to whitelist only the secretary and
Student Council leaders by checking the approver user's role/position before
creating the CommunityService (or add a custom validation Rule::exists with a
where clause or a post-validate check); if the approver user is not in the
allowed roles (e.g., secretary or student council leader) return a validation
error and do not proceed to CommunityService::create (keep Semester::current()
and other fields unchanged).
♻️ Duplicate comments (5)
resources/views/student-council/community-service/request.blade.php (1)

2-4: ⚠️ Potential issue | 🟠 Major

Still unresolved: filter out a missing secretary before building $approvers.

User::secretary() is nullable, so the secretariat branch can still pass [null] into x-input.select when the post is vacant. Please drop null entries before assigning the options array.

Suggested fix
-    $approvers = $isForSecretariat
-                ? [\App\Models\User::secretary()]
+    $approvers = $isForSecretariat
+                ? array_values(array_filter([\App\Models\User::secretary()]))
                 : \App\Models\User::studentCouncilLeaders()->toArray();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/student-council/community-service/request.blade.php` around
lines 2 - 4, The $approvers assignment can include a null from
\App\Models\User::secretary(), so when building $approvers (the ternary using
$isForSecretariat) filter out null entries before passing to the select
component; change the secretariat branch to produce an array without nulls
(e.g., wrap the result in an array filter) so x-input.select never receives
[null].
app/Http/Controllers/StudentsCouncil/CommunityServiceController.php (2)

123-124: ⚠️ Potential issue | 🔴 Critical

Guard the nullable approvingRole() result before reading ->name.

approvingRole() explicitly returns null on lines 107-108, but Line 124 dereferences it unconditionally. Please add a null check here rather than assuming the data is always pristine.

Suggested fix
-        $isForSecretariat = Role::SECRETARY == $approvingRole->name;
+        $isForSecretariat = !is_null($approvingRole) && $approvingRole->name === Role::SECRETARY;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 123 - 124, The code dereferences the possibly-null result of
approvingRole() when reading ->name; update the CommunityServiceController logic
to guard against a null approvingRole by checking the return of
self::approvingRole($approver) before accessing ->name (e.g., assign to
$approvingRole, if null set $isForSecretariat = false or handle appropriately),
and then compare $approvingRole->name to Role::SECRETARY only when
$approvingRole is not null.

138-140: ⚠️ Potential issue | 🟠 Major

Guard educationalInformation before reading the Neptun code.

Line 139 assumes every requester has an attached educational-information record. If that relation is missing, certificate generation dies with a 500 instead of returning a user-facing error.

Minimal guard
         $requester = $communityService->requester;
+        if (is_null($requester->educationalInformation)) {
+            return back()->with('error', 'A kérvényező tanulmányi adatai hiányoznak.');
+        }
         $approver = Auth::user();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 138 - 140, The code in CommunityServiceController is accessing
$requester->educationalInformation->neptun without checking the relation; update
the requester_neptun assignment to guard the relation (e.g., check
$requester->educationalInformation exists) and either provide a safe fallback
(empty string/null) or return a user-facing error before certificate generation;
ensure this change is applied where 'requester_neptun' is set so missing
educationalInformation does not produce a 500.
app/Utils/LatexHelper.php (2)

15-23: ⚠️ Potential issue | 🟠 Major

\symbol{mb_ord(...)} is still not Unicode-safe for pdfLaTeX.

Lines 18-22 only special-case lowercase ő/ű and the en dash. Uppercase Ő/Ű, curly quotes, and any other character above 255 still become \symbol{336} / \symbol{8221}, which pdfLaTeX will reject. Please either move to a Unicode-capable engine or map those characters to proper LaTeX escapes instead of feeding raw mb_ord() values into \symbol.

In pdfLaTeX, what numeric range is valid for \symbol{<number>} / \char, and what is the recommended way to emit UTF-8 characters such as Ő, ő, Ű, or curly quotes from application input?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` around lines 15 - 23, convertCharToSymbol
currently emits \symbol{mb_ord(...)} for any non-space/en-dash/ő/ű which fails
for Unicode codepoints >255 and misses uppercase Ő/Ű and curly quotes; update
convertCharToSymbol to either (a) switch the project to a Unicode-capable LaTeX
engine (XeLaTeX/LuaLaTeX) and emit characters directly as UTF-8, or (b)
implement a mapping table inside convertCharToSymbol that translates known
Unicode characters (e.g., Ő, Ű, curly quotes, en/em dashes, etc.) to proper
LaTeX escapes (e.g., \H{O}, \H{u}, ``/'' or the appropriate commands) and only
fall back to \symbol{...} for codepoints <=255; remove use of raw mb_ord for
>255 codepoints and ensure convertCharToSymbol handles both lowercase and
uppercase variants.

65-71: ⚠️ Potential issue | 🟠 Major

Honour the debug switch and fail fast when compilation produces no PDF.

Line 66 still runs Commands::latexToPdf() even when the method later decides to return the .tex file, and app/Console/Commands.php:27-33 only returns stdout. As written, this can both ignore commands.run_in_debug and return a dead $pathPdf after a LaTeX error.

Suggested fix
-        // TODO: figure out result
-        Commands::latexToPdf($pathTex, $outputDir);
-
         if (config('app.debug') && !config('commands.run_in_debug')) {
             return $pathTex;
-        } else {
-            return $pathPdf;
         }
+
+        $latexOutput = Commands::latexToPdf($pathTex, $outputDir);
+        if (!file_exists($pathPdf)) {
+            throw new \RuntimeException('LaTeX compilation failed: ' . $latexOutput);
+        }
+
+        return $pathPdf;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Utils/LatexHelper.php` around lines 65 - 71, The code currently calls
Commands::latexToPdf($pathTex, $outputDir) unconditionally and then sometimes
returns $pathTex for debug, which can ignore the debug switch and return a
non-existent $pathPdf after a LaTeX error; change LatexHelper to first check
config('app.debug') && !config('commands.run_in_debug') and return $pathTex
immediately without calling Commands::latexToPdf, otherwise call
Commands::latexToPdf and capture its result (stdout/return value) and/or verify
that $pathPdf exists and is non-empty, and if compilation failed throw/return a
fast failure (exception or falsy) rather than returning a dead $pathPdf;
reference Commands::latexToPdf, $pathTex, $pathPdf and app/Console/Commands.php
behavior when implementing these checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 101-107: The approvingRole lookup in approvingRole(User $user)
reads the approver's current Student Council role live (via
$user->roles()->where(...)->first()->pivot->object), which can change later;
instead persist a snapshot of the approver's title/object at creation/approval
time and read that stored value. Add a persistent field on the CommunityService
(or related) model (e.g., approving_title or approving_role_object and/or
approving_role_id) and a migration to persist the approver snapshot during the
certificate creation/approval flow (where approvals are recorded), update the
code paths that create/approve certificates to save the approver’s role/object
there, and finally change approvingRole to return the stored snapshot field(s)
rather than querying $user->roles() live; keep
Role::SECRETARY/Role::STUDENT_COUNCIL checks only when determining what to
store.

In `@resources/views/student-council/community-service/request.blade.php`:
- Around line 17-20: The unordered list in the Blade view (the <ul
class="browser-default"> block) still contains placeholder list items ("..." and
the inline red note "(ezt beszéljük majd meg pontosan)"); replace those
placeholder <li> entries with the final secretariat case items (Hungarian copy)
and remove the internal red note so the rendered page clearly lists which cases
belong to the secretariat; update the list content inside the same <ul
class="browser-default"> element in request.blade.php and ensure the items are
properly localized/escaped for production.

---

Outside diff comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 53-66: The current validation only ensures approver exists in
users but not that they are an allowed recipient; update the validation logic
around request->validate (the approver rule) to whitelist only the secretary and
Student Council leaders by checking the approver user's role/position before
creating the CommunityService (or add a custom validation Rule::exists with a
where clause or a post-validate check); if the approver user is not in the
allowed roles (e.g., secretary or student council leader) return a validation
error and do not proceed to CommunityService::create (keep Semester::current()
and other fields unchanged).

---

Duplicate comments:
In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php`:
- Around line 123-124: The code dereferences the possibly-null result of
approvingRole() when reading ->name; update the CommunityServiceController logic
to guard against a null approvingRole by checking the return of
self::approvingRole($approver) before accessing ->name (e.g., assign to
$approvingRole, if null set $isForSecretariat = false or handle appropriately),
and then compare $approvingRole->name to Role::SECRETARY only when
$approvingRole is not null.
- Around line 138-140: The code in CommunityServiceController is accessing
$requester->educationalInformation->neptun without checking the relation; update
the requester_neptun assignment to guard the relation (e.g., check
$requester->educationalInformation exists) and either provide a safe fallback
(empty string/null) or return a user-facing error before certificate generation;
ensure this change is applied where 'requester_neptun' is set so missing
educationalInformation does not produce a 500.

In `@app/Utils/LatexHelper.php`:
- Around line 15-23: convertCharToSymbol currently emits \symbol{mb_ord(...)}
for any non-space/en-dash/ő/ű which fails for Unicode codepoints >255 and misses
uppercase Ő/Ű and curly quotes; update convertCharToSymbol to either (a) switch
the project to a Unicode-capable LaTeX engine (XeLaTeX/LuaLaTeX) and emit
characters directly as UTF-8, or (b) implement a mapping table inside
convertCharToSymbol that translates known Unicode characters (e.g., Ő, Ű, curly
quotes, en/em dashes, etc.) to proper LaTeX escapes (e.g., \H{O}, \H{u}, ``/''
or the appropriate commands) and only fall back to \symbol{...} for codepoints
<=255; remove use of raw mb_ord for >255 codepoints and ensure
convertCharToSymbol handles both lowercase and uppercase variants.
- Around line 65-71: The code currently calls Commands::latexToPdf($pathTex,
$outputDir) unconditionally and then sometimes returns $pathTex for debug, which
can ignore the debug switch and return a non-existent $pathPdf after a LaTeX
error; change LatexHelper to first check config('app.debug') &&
!config('commands.run_in_debug') and return $pathTex immediately without calling
Commands::latexToPdf, otherwise call Commands::latexToPdf and capture its result
(stdout/return value) and/or verify that $pathPdf exists and is non-empty, and
if compilation failed throw/return a fast failure (exception or falsy) rather
than returning a dead $pathPdf; reference Commands::latexToPdf, $pathTex,
$pathPdf and app/Console/Commands.php behavior when implementing these checks.

In `@resources/views/student-council/community-service/request.blade.php`:
- Around line 2-4: The $approvers assignment can include a null from
\App\Models\User::secretary(), so when building $approvers (the ternary using
$isForSecretariat) filter out null entries before passing to the select
component; change the secretariat branch to produce an array without nulls
(e.g., wrap the result in an array filter) so x-input.select never receives
[null].

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e59f178f-9ab7-4262-b035-990e76852502

📥 Commits

Reviewing files that changed from the base of the PR and between 107f095 and 4504e7f.

📒 Files selected for processing (4)
  • app/Http/Controllers/StudentsCouncil/CommunityServiceController.php
  • app/Utils/LatexHelper.php
  • resources/views/latex/community-service.blade.php
  • resources/views/student-council/community-service/request.blade.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • resources/views/latex/community-service.blade.php

Comment on lines +101 to +107
private static function approvingRole(User $user): Role|RoleObject|null
{
if ($user->hasRole(Role::SECRETARY)) {
return Role::secretary();
} elseif ($user->hasRole(Role::STUDENT_COUNCIL)) {
return $user->roles()->where('role_id', Role::studentsCouncil()->id)->first()->pivot->object;
} else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Persist the approving title instead of looking it up live.

Line 106 takes the approver's current first Student Council role. That makes the certificate title arbitrary for multi-role users, and it will drift if the approver's role changes after approval. For an official certificate, this wants a stored role/object snapshot from creation or approval time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/StudentsCouncil/CommunityServiceController.php` around
lines 101 - 107, The approvingRole lookup in approvingRole(User $user) reads the
approver's current Student Council role live (via
$user->roles()->where(...)->first()->pivot->object), which can change later;
instead persist a snapshot of the approver's title/object at creation/approval
time and read that stored value. Add a persistent field on the CommunityService
(or related) model (e.g., approving_title or approving_role_object and/or
approving_role_id) and a migration to persist the approver snapshot during the
certificate creation/approval flow (where approvals are recorded), update the
code paths that create/approve certificates to save the approver’s role/object
there, and finally change approvingRole to return the stored snapshot field(s)
rather than querying $user->roles() live; keep
Role::SECRETARY/Role::STUDENT_COUNCIL checks only when determining what to
store.

Comment on lines +17 to +20
<ul class="browser-default">
<li>... <span style="color: red;">(ezt beszéljük majd meg pontosan)</span></li>
<li>...</li>
<li>...</li>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Please replace the placeholder secretariat list before release.

Lines 18-20 still render literal ... entries and an internal red note, so the new flow does not actually tell applicants which cases belong to the secretariat.

If useful, I can draft the final Hungarian copy in a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/views/student-council/community-service/request.blade.php` around
lines 17 - 20, The unordered list in the Blade view (the <ul
class="browser-default"> block) still contains placeholder list items ("..." and
the inline red note "(ezt beszéljük majd meg pontosan)"); replace those
placeholder <li> entries with the final secretariat case items (Hungarian copy)
and remove the internal red note so the rendered page clearly lists which cases
belong to the secretariat; update the list content inside the same <ul
class="browser-default"> element in request.blade.php and ensure the items are
properly localized/escaped for production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generating LaTeX certificates from community service items

2 participants