feat: implement barangay system phases 2-14
Some checks failed
tests / PHP 8.2 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.3 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.4 (swoole-6.0) (push) Has been cancelled

Complete adaptation from BukidBountyApp to Philippine barangay governance:

- Barangay models: Resident, Household, HouseholdMember, Blotter, BlotterHearing,
  DocumentRequest, RequestPayment, RequestType, BarangayProject, BarangayBudget
- Controllers: ResidentController, HouseholdController, BlotterController,
  BlotterHearingController, DocumentRequestController, RequestTypeController,
  ProjectController, BudgetController, QRPHController, AdminConsoleController,
  UserController, FileController, ChapterController, LoginController
- Vue pages: Home, ManageResidents, ResidentProfile, ManageHouseholds, ManageBlotters,
  BlotterDetail, RequestDocument, ManageDocumentRequests, DocumentRequestDetail,
  ManageRequestTypes, ManageProjects, BudgetLedger, AdminConsole
- Barangay roles: PunongBarangay, Kagawad, Secretary, Treasurer, SK, Tanod, BHW, Staff, Resident
- UserPermissions matrix rewritten with barangay-specific permission mappings
- VueRouteMap replaced with barangay SPA routes
- UserActions enum references corrected across all controllers
- Removed all market/cooperative/POS/subscription code and models
This commit is contained in:
Jonathan Sykes
2026-06-07 03:09:09 +08:00
parent 19fec0933b
commit fbb7e3ff37
234 changed files with 5582 additions and 39457 deletions

View File

@@ -0,0 +1,223 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Hypervel\Http\Request;
use App\Models\FileList;
use App\Models\FileContent;
use Hypervel\Http\UploadedFile;
use Hypervel\Support\Facades\File;
use Hypervel\Support\Facades\Storage;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\DB;
use \PDO;
use Hypervel\Support\Facades\Response;
use Hypervel\Support\Carbon;
use App\Http\Controllers\Helpers\Permissions\UserPermissions;
use App\Enums\UserActions;
class FileController
{
private static function isLikelyBinary($string, $threshold = 0.3): bool
{
if (strpos($string, "\x00") !== false) return true;
$len = strlen($string);
if ($len === 0) return false;
$nonPrintable = preg_match_all('~[^\x09\x0A\x0D\x20-\x7E]~', $string);
return ($nonPrintable / $len) > $threshold;
}
private static function insertFileContentPostgresPDO(array $data)
{
$pdo = DB::getPdo();
$stmt = $pdo->prepare('
INSERT INTO file_content
(hashkey, filehash, titlename, description, size_in_bytes, content, filelocation, created_by, updated_by, details, created_at, updated_at)
VALUES
(:hashkey, :filehash, :titlename, :description, :size_in_bytes, :content, :filelocation, :created_by, :updated_by, :details, :created_at, :updated_at)
');
$now = now()->toDateTimeString();
$stmt->bindParam(':hashkey', $data['hashkey']);
$stmt->bindParam(':filehash', $data['filehash']);
$stmt->bindParam(':titlename', $data['titlename']);
$stmt->bindParam(':description', $data['description']);
$stmt->bindParam(':size_in_bytes', $data['size_in_bytes']);
$stmt->bindParam(':content', $data['content'], PDO::PARAM_LOB);
$stmt->bindParam(':filelocation', $data['filelocation']);
$stmt->bindParam(':created_by', $data['created_by']);
$stmt->bindParam(':updated_by', $data['updated_by']);
$detailsJson = json_encode($data['details']);
$stmt->bindParam(':details', $detailsJson);
$stmt->bindParam(':created_at', $now);
$stmt->bindParam(':updated_at', $now);
$stmt->execute();
$id = DB::getPdo()->lastInsertId();
return FileContent::find($id);
}
private static function insertFileContentSql(array $data)
{
$now = Carbon::now();
$data['content'] = base64_encode($data['content']);
$data['details'] = json_encode($data['details']);
$data['created_at'] = $now;
$data['updated_at'] = $now;
return FileContent::create($data);
}
private static function insertFileContent(array $data)
{
$driver = DB::connection()->getDriverName();
if ($driver === 'pgsql') return self::insertFileContentPostgresPDO($data);
if ($driver === 'mysql') return self::insertFileContentSql($data);
throw new \RuntimeException("Unsupported database driver: {$driver}");
}
private static function uploadFileContent(UploadedFile|string $fileData, string $title, ?string $description = null, ?array $details = [])
{
if ($fileData instanceof UploadedFile) {
$fileHash = hash_file('sha256', $fileData->getRealPath());
$fileSize = $fileData->getSize();
$fileContent = File::get($fileData->getRealPath());
$path = $fileData->storeAs('files', $fileHash);
} elseif (is_string($fileData) && file_exists($fileData)) {
$fileHash = hash_file('sha256', $fileData);
$fileSize = filesize($fileData);
$fileContent = file_get_contents($fileData);
$path = Storage::put("files/{$fileHash}", $fileContent);
} elseif (self::isLikelyBinary($fileData)) {
$fileHash = hash('sha256', $fileData);
$fileSize = strlen($fileData);
$fileContent = $fileData;
$path = Storage::put("files/{$fileHash}", $fileContent);
} else {
throw new \InvalidArgumentException('Invalid file data provided.');
}
$existing = FileContent::where('filehash', $fileHash)->first();
if ($existing) return $existing;
$hashKey = hash('sha256', uniqid((string) now(), true));
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimetype = $finfo->buffer($fileContent);
return self::insertFileContent([
'hashkey' => $hashKey,
'filehash' => $fileHash,
'titlename' => $title,
'description' => $description,
'size_in_bytes' => $fileSize,
'content' => $fileContent,
'filelocation' => $path,
'created_by' => Auth::id(),
'updated_by' => Auth::id(),
'details' => $details ?? [],
'mimetype' => $mimetype,
]);
}
private static function insertFileList(int $contentuid, string $title, string $filename, string $description, $categories, $details, $tags = [], $hidden = 0, ?string $file_type = null)
{
if (!FileContent::where('id', $contentuid)->exists()) {
throw new \Exception("File Content does not exist");
}
return FileList::create([
'contentuid' => $contentuid,
'hashkey' => hash('sha256', uniqid((string) now(), true)),
'title' => $title,
'filename' => $filename,
'description' => $description,
'categories' => $categories,
'details' => $details,
'tags' => $tags,
'hidden' => $hidden,
'file_type' => $file_type,
'is_public' => false,
'created_by' => Auth::id(),
'updated_by' => Auth::id(),
]);
}
public static function uploadFileList(string|UploadedFile $fileData, string $title, string $filename, ?string $description = null, ?array $details = [], $categories = null, $tags = [], $hidden = 0, ?string $file_type = null)
{
try {
$fileContent = self::uploadFileContent($fileData, $title, $description, $details);
return self::insertFileList($fileContent->id, $title, $filename, $description, $categories, $details, $tags, $hidden, $file_type);
} catch (\Throwable $th) {
return Response::json($th->getMessage(), 500);
}
}
public static function viewFilebyFileListHash(string $filelist_hash)
{
$filelist = FileList::where('hashkey', $filelist_hash)->first();
if (!$filelist) abort(404, 'File not found');
$cdnUrl = trim((string) ($filelist->cdn_url ?? ''));
if ($cdnUrl !== '') return redirect($cdnUrl);
$fileContent = $filelist->fileContent;
if (!$fileContent) abort(404, 'File content not found');
$content = $fileContent->content;
$driver = DB::connection()->getDriverName();
if (is_resource($content) && $driver !== 'mysql') $content = stream_get_contents($content);
if ($driver === 'mysql') $content = base64_decode($content);
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($content);
$mimeMap = [
'image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif',
'image/webp' => 'webp', 'application/pdf' => 'pdf', 'text/plain' => 'txt',
'application/zip' => 'zip', 'application/json' => 'json',
];
$extension = $mimeMap[$mimeType] ?? pathinfo($filelist->filename, PATHINFO_EXTENSION);
$filename = pathinfo($filelist->filename, PATHINFO_FILENAME) . '.' . $extension;
return Response::make($content, 200, [
'Content-Type' => $mimeType,
'Content-Disposition' => 'inline; filename="' . $filename . '"',
'Content-Length' => strlen($content),
]);
}
public static function generateURLforFileListHash(string $filelist_hashkey): string
{
$cdnUrl = FileList::where('hashkey', $filelist_hashkey)->value('cdn_url');
if (is_string($cdnUrl) && trim($cdnUrl) !== '') return $cdnUrl;
return "/RequestData/File/$filelist_hashkey";
}
public static function UploadFilefromRequest(Request $request, string $category)
{
if (!Auth::check() || !UserPermissions::isActionPermitted(Auth::user()->acct_type, UserActions::UploadAllFiles)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
if (!$request->hasFile('file')) {
return response()->json(false, 400);
}
$file = $request->file('file');
$filename = $file->getClientFilename();
$result = self::uploadFileList($file, '', $filename ?? '', '', [], $category, [], 0, null);
try {
if (is_string($result->hashkey) && !empty($result->hashkey)) {
return response()->json([
'success' => true,
'hashkey' => $result->hashkey,
'message' => 'File uploaded successfully',
'url' => $result->resolvedUrl(),
'name' => $filename,
], 200);
}
throw new \Exception("File upload failed");
} catch (\Throwable $th) {
return response()->json(['success' => false, 'error' => 'File upload failed'], 500);
}
}
}