Autosave: 20260214-075612

This commit is contained in:
Flatlogic Bot 2026-02-14 07:56:12 +00:00
parent 29bb162fcd
commit c1267b50a7
19 changed files with 4017 additions and 1332 deletions

View File

@ -5,10 +5,16 @@ require_once 'auth.php';
require_login();
require_once '../db/config.php';
$pdo = db();
require_once '../includes/fpdf/fpdf.php';
// Use tFPDF for Unicode/Arabic support
require_once '../includes/fpdf/tfpdf.php';
require_once '../includes/fpdf/font/unifont/ttfonts.php';
require_once '../includes/I18N/Arabic.php';
require_once '../mail/MailService.php';
require_once 'i18n.php';
use I18N\Arabic;
// Get case ID from URL
if (!isset($_GET['case_id']) || !is_numeric($_GET['case_id'])) {
die('Invalid Case ID');
@ -33,115 +39,60 @@ $logs_stmt = $pdo->prepare('SELECT al.*, u.email FROM audit_logs al LEFT JOIN us
$logs_stmt->execute([$case_id]);
$audit_logs = $logs_stmt->fetchAll(PDO::FETCH_ASSOC);
// ####################################################################################
// ## Fake Arabic Class for RTL text
// ####################################################################################
class PDF_Arabic extends FPDF {
protected $CurrentFont;
// PDF Generation Class
class PDF extends tFPDF
{
private $arabic;
function __construct($orientation='P', $unit='mm', $size='A4') {
parent::__construct($orientation, $unit, $size);
$this->AddFont('Amiri','','amiri.php');
$this->AddFont('Amiri','B','amirib.php');
}
function Write($h, $txt, $link='', $fill=false, $align='') {
$txt = $this->Convert_ar ($txt);
parent::Write($h, $txt, $link, $fill, $align);
}
function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') {
$txt = $this->Convert_ar ($txt);
parent::Cell($w, $h, $txt, $border, $ln, $align, $fill, $link);
}
function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) {
$txt = $this->Convert_ar ($txt);
parent::MultiCell($w, $h, $txt, $border, $align, $fill);
}
function Convert_ar($txt) {
$p = $this->GetStringWidth($txt);
$p = ($this->w - $p) /2;
$nb = strlen($txt);
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$sep = '-';
$w = '';
$len = 0;
$h = $this->CurrentFont['h'];
while ($i < $nb) {
$c = $txt[$i];
if ($c == '(') {
$i++;
$j = $i;
while ($i < $nb && $txt[$i] != ')')
$i++;
$w .= substr($txt, $j, $i - $j) . ' ';
$i++;
continue;
} elseif ($c == ')') {
$i++;
continue;
}
if (ord($c) < 192) {
if ($c == ' ') {
if ($l > 0) {
$w .= $this->sub_str($txt, $ns, $l) . $sep;
$ns = $i + 1;
$l = 0;
} else {
$ns++;
}
} else
$l++;
$i++;
}
else {
$i += 2;
$l += 2;
}
}
if ($l > 0)
$w .= $this->sub_str($txt, $ns, $l);
$this->rtl = false;
return $w;
}
function sub_str($str, $from, $len) {
$s = '';
$i = $from;
while ($i < $from + $len) {
if (ord($str[$i]) < 128)
$s .= $str[$i++];
else {
$s .= $str[$i++] . $str[$i++];
}
}
return $s;
function __construct($orientation='P', $unit='mm', $size='A4') {
parent::__construct($orientation, $unit, $size);
// Load Amiri font (Unicode)
$this->AddFont('Amiri', '', 'amiri.ttf', true);
$this->AddFont('Amiri', 'B', 'amiri.ttf', true); // Reuse regular for bold if bold TTF missing
$this->arabic = new Arabic();
}
}
function shapeText($text) {
// Apply Arabic shaping (Ligatures + Reversal for RTL)
// This is necessary because PDF writes characters Left-to-Right.
// For Arabic, we must reverse the string (Last char first) after shaping.
if (preg_match('/\p{Arabic}/u', $text)) {
return $this->arabic->utf8Glyphs($text);
}
return $text;
}
function formatMoney($amount) {
$currency = __('OMR');
$formatted = number_format($amount, 3);
if (get_current_lang() === 'ar') {
// For Arabic, we want the visual order to be: Number then Currency (Reading RTL: Currency Number)
// But since we print LTR, we place Number on the Left, and reversed Currency on the Right.
// E.g. "123 .ع.ر"
return $formatted . ' ' . $this->shapeText($currency);
}
// For English: "OMR 123"
return $currency . ' ' . $formatted;
}
// PDF Generation Class
class PDF extends PDF_Arabic
{
function Header() {
if (file_exists('../assets/images/logo_1770967720.jpg')) {
$this->Image('../assets/images/logo_1770967720.jpg', 10, 6, 30);
}
$this->SetFont('Amiri', 'B', 15);
$this->Cell(80);
$this->Cell(30, 10, __('Case Report'), 0, 0, 'C');
$title = $this->shapeText(__('Case Report'));
$this->Cell(30, 10, $title, 0, 0, 'C');
$this->Ln(20);
}
function Footer() {
$this->SetY(-15);
$this->SetFont('Amiri', 'B', 8);
$this->Cell(0, 10, __('Page') . ' ' . $this->PageNo() . '/{nb}', 0, 0, 'C');
$pageTxt = $this->shapeText(__('Page') . ' ' . $this->PageNo() . '/{nb}');
$this->Cell(0, 10, $pageTxt, 0, 0, 'C');
}
}
@ -152,6 +103,8 @@ function generate_pdf($case, $donations, $audit_logs)
$pdf->AddPage();
$pdf->SetFont('Amiri', '', 12);
$is_ar = (get_current_lang() === 'ar');
// Calculate total donations
$total_donations = 0;
foreach ($donations as $donation) {
@ -162,56 +115,96 @@ function generate_pdf($case, $donations, $audit_logs)
// Case Details
$pdf->SetFont('Amiri', 'B', 16);
$pdf->Cell(0, 10, __('Case Details'), 0, 1, 'R');
$pdf->Cell(0, 10, $pdf->shapeText(__('Case Details')), 0, 1, 'R');
$pdf->SetFont('Amiri', '', 12);
$pdf->Cell(40, 10, __('Case ID:'), 0, 0,'R'); $pdf->Cell(0, 10, $case['id'], 0, 1, 'R');
$pdf->Cell(40, 10, __('Title:'), 0, 0, 'R'); $pdf->Cell(0, 10, (get_current_lang() === 'ar' ? $case['title_ar'] : $case['title_en']), 0, 1, 'R');
$pdf->Cell(40, 10, __('Category:'), 0, 0,'R'); $pdf->Cell(0, 10, (get_current_lang() === 'ar' ? $case['category_name_ar'] : $case['category_name']), 0, 1, 'R');
$pdf->Cell(40, 10, __('Goal Amount:'), 0, 0,'R'); $pdf->Cell(0, 10, __('OMR') . ' ' . number_format($case['goal'], 3), 0, 1, 'R');
$pdf->Cell(40, 10, __('Raised Amount:'), 0, 0,'R'); $pdf->Cell(0, 10, __('OMR') . ' ' . number_format($case['raised'], 3), 0, 1, 'R');
$pdf->Cell(40, 10, __('Total Collected:'), 0, 0,'R'); $pdf->Cell(0, 10, __('OMR') . ' ' . number_format($total_donations, 3), 0, 1, 'R');
$pdf->Cell(40, 10, __('Status:'), 0, 0,'R'); $pdf->Cell(0, 10, __($case['status']), 0, 1, 'R');
$rows = [
[__('Case ID:'), $case['id']],
[__('Title:'), ($is_ar ? $case['title_ar'] : $case['title_en'])],
[__('Category:'), ($is_ar ? $case['category_name_ar'] : $case['category_name'])],
// Pass false as 3rd arg to skip re-shaping for formatted money
[__('Goal Amount:'), $pdf->formatMoney($case['goal']), false],
[__('Raised Amount:'), $pdf->formatMoney($case['raised']), false],
[__('Total Collected:'), $pdf->formatMoney($total_donations), false],
[__('Status:'), __($case['status'])],
];
foreach ($rows as $row) {
$label = $pdf->shapeText($row[0]);
$should_shape = isset($row[2]) ? $row[2] : true;
$value = $should_shape ? $pdf->shapeText($row[1]) : $row[1];
if ($is_ar) {
// RTL: Value (Left), Label (Right)
$pdf->Cell(150, 10, $value, 0, 0, 'R');
$pdf->Cell(40, 10, $label, 0, 1, 'R');
} else {
// LTR: Label (Left), Value (Right)
$pdf->Cell(40, 10, $label, 0, 0, 'R');
$pdf->Cell(0, 10, $value, 0, 1, 'R');
}
}
$pdf->Ln(10);
// Donations
$pdf->SetFont('Amiri', 'B', 16);
$pdf->Cell(0, 10, __('Donations'), 0, 1, 'R');
$pdf->Cell(0, 10, $pdf->shapeText(__('Donations')), 0, 1, 'R');
$pdf->SetFont('Amiri', 'B', 10);
$pdf->Cell(25, 7, __('ID'), 1, 0, 'C');
$pdf->Cell(50, 7, __('Donor'), 1, 0, 'C');
$pdf->Cell(35, 7, __('Amount'), 1, 0, 'C');
$pdf->Cell(40, 7, __('Date'), 1, 0, 'C');
$pdf->Cell(30, 7, __('Status'), 1, 1, 'C');
$headers = [
['w' => 25, 't' => __('ID')],
['w' => 50, 't' => __('Donor')],
['w' => 35, 't' => __('Amount')],
['w' => 40, 't' => __('Date')],
['w' => 30, 't' => __('Status')]
];
foreach ($headers as $h) {
$pdf->Cell($h['w'], 7, $pdf->shapeText($h['t']), 1, 0, 'C');
}
$pdf->Ln();
$pdf->SetFont('Amiri', '', 10);
if (empty($donations)) {
$pdf->Cell(180, 10, __('No donations for this case.'), 1, 1, 'C');
$pdf->Cell(180, 10, $pdf->shapeText(__('No donations for this case.')), 1, 1, 'C');
} else {
foreach ($donations as $donation) {
$pdf->Cell(25, 7, $donation['id'], 1, 0, 'C');
$pdf->Cell(50, 7, htmlspecialchars($donation['donor_name']), 1, 0, 'R');
$pdf->Cell(35, 7, __('OMR') . ' ' . number_format($donation['amount'], 3), 1, 0, 'R');
$pdf->Cell(50, 7, $pdf->shapeText($donation['donor_name']), 1, 0, 'R');
$pdf->Cell(35, 7, $pdf->formatMoney($donation['amount']), 1, 0, 'R');
$pdf->Cell(40, 7, $donation['created_at'], 1, 0, 'C');
$pdf->Cell(30, 7, __($donation['status']), 1, 1, 'C');
$pdf->Cell(30, 7, $pdf->shapeText(__($donation['status'])), 1, 1, 'C');
}
}
$pdf->Ln(10);
// History
$pdf->SetFont('Amiri', 'B', 16);
$pdf->Cell(0, 10, __('Case History'), 0, 1, 'R');
$pdf->Cell(0, 10, $pdf->shapeText(__('Case History')), 0, 1, 'R');
$pdf->SetFont('Amiri', 'B', 10);
$pdf->Cell(20, 7, __('Log ID'), 1, 0, 'C');
$pdf->Cell(30, 7, __('User'), 1, 0, 'C');
$pdf->Cell(80, 7, __('Action'), 1, 0, 'C');
$pdf->Cell(50, 7, __('Timestamp'), 1, 1, 'C');
$headers_log = [
['w' => 20, 't' => __('Log ID')],
['w' => 30, 't' => __('User')],
['w' => 80, 't' => __('Action')],
['w' => 50, 't' => __('Timestamp')]
];
foreach ($headers_log as $h) {
$pdf->Cell($h['w'], 7, $pdf->shapeText($h['t']), 1, 0, 'C');
}
$pdf->Ln();
$pdf->SetFont('Amiri', '', 9);
if (empty($audit_logs)) {
$pdf->Cell(180, 10, __('No history for this case.'), 1, 1, 'C');
$pdf->Cell(180, 10, $pdf->shapeText(__('No history for this case.')), 1, 1, 'C');
} else {
foreach ($audit_logs as $log) {
$pdf->Cell(20, 7, $log['id'], 1, 0, 'C');
$pdf->Cell(30, 7, htmlspecialchars($log['email']), 1, 0, 'C');
$pdf->Cell(80, 7, htmlspecialchars($log['action']), 1, 0, 'R');
$pdf->Cell(30, 7, $pdf->shapeText($log['email']), 1, 0, 'C');
$pdf->Cell(80, 7, $pdf->shapeText($log['action']), 1, 0, 'R');
$pdf->Cell(50, 7, $log['created_at'], 1, 1, 'C');
}
}

View File

@ -190,6 +190,8 @@ $is_rtl = (get_current_lang() === 'ar');
<span class="badge bg-success"><?= __('Active') ?></span>
<?php elseif ($case['status'] === 'paused'): ?>
<span class="badge bg-warning text-dark"><?= __('Paused') ?></span>
<?php elseif ($case['status'] === 'completed'): ?>
<span class="badge bg-primary"><?= __('Completed') ?></span>
<?php else: ?>
<span class="badge bg-danger"><?= __('Disabled') ?></span>
<?php endif; ?>
@ -277,6 +279,7 @@ $is_rtl = (get_current_lang() === 'ar');
<option value="active"><?= __('Active') ?></option>
<option value="paused"><?= __('Paused') ?></option>
<option value="disabled"><?= __('Disabled') ?></option>
<option value="completed"><?= __('Completed') ?></option>
</select>
</div>
</div>

View File

@ -35,6 +35,7 @@ $texts = [
'admin_panel' => 'Admin Panel',
'lang_name' => 'العربية',
'lang_code' => 'ar',
'goal_reached' => 'Goal Reached! Campaign Completed.',
],
'ar' => [
'title' => 'تفاصيل الحالة',
@ -63,6 +64,7 @@ $texts = [
'admin_panel' => 'لوحة التحكم',
'lang_name' => 'English',
'lang_code' => 'en',
'goal_reached' => 'تم تحقيق الهدف! الحملة مكتملة.',
]
];
@ -81,7 +83,7 @@ if ($case_id === 0) {
$stmt = $pdo->prepare("SELECT c.*, cat.name_en as cat_name_en, cat.name_ar as cat_name_ar
FROM cases c
LEFT JOIN categories cat ON c.category_id = cat.id
WHERE c.id = :id AND c.status = 'active'");
WHERE c.id = :id AND (c.status = 'active' OR c.status = 'completed')");
$stmt->execute(['id' => $case_id]);
$case = $stmt->fetch();
}
@ -139,11 +141,18 @@ require_once 'includes/header.php';
</div>
<div class="d-grid mt-4">
<?php if ($case['status'] === 'active'): ?>
<button class="btn btn-donate py-3 fs-5" data-bs-toggle="modal" data-bs-target="#donateModal"
data-case-id="<?= $case['id'] ?>"
data-case-title="<?= htmlspecialchars($lang === 'en' ? $case['title_en'] : $case['title_ar']) ?>">
<?= $t['donate_now'] ?>
</button>
<?php else: ?>
<div class="alert alert-success text-center fw-bold py-3 fs-5 mb-0 rounded-4">
<i class="bi bi-check-circle-fill me-2"></i>
<?= $t['goal_reached'] ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
@ -168,6 +177,7 @@ require_once 'includes/header.php';
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-4">
<form action="checkout.php" method="POST">
<input type="hidden" name="lang" value="<?= $lang ?>">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= $t['modal_title'] ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>

View File

@ -17,21 +17,22 @@ $don = $stmt->fetch();
if (!$don) exit('Donation not found or not completed.');
$org = $pdo->query("SELECT * FROM org_profile LIMIT 1")->fetch();
$orgName = $org['name_en'] ?? 'Organization';
// Prefer Arabic name if available, otherwise English or default
$orgName = $org['name_ar'] ?? ($org['name_en'] ?? 'المؤسسة');
// This is a simple HTML certificate that can be printed or saved as PDF by the user
?>
<!DOCTYPE html>
<html lang="en">
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Certificate - <?= htmlspecialchars($orgName) ?></title>
<title>شهادة تبرع - <?= htmlspecialchars($orgName) ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Inter:wght@400;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Cairo:wght@400;600;700&display=swap" rel="stylesheet">
<style>
body { background: #f3f4f6; padding: 40px 20px; font-family: 'Inter', sans-serif; }
body { background: #f3f4f6; padding: 40px 20px; font-family: 'Cairo', sans-serif; }
.certificate-container {
max-width: 800px;
margin: 0 auto;
@ -49,13 +50,15 @@ $orgName = $org['name_en'] ?? 'Organization';
border: 2px solid #059669;
pointer-events: none;
}
.cert-header { font-family: 'Cinzel', serif; font-size: 3rem; color: #111827; margin-bottom: 10px; }
.cert-sub { font-size: 1.25rem; color: #059669; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 40px; }
.cert-logo { margin-bottom: 20px; }
.cert-logo img { max-height: 100px; }
.cert-header { font-family: 'Amiri', serif; font-size: 3rem; color: #111827; margin-bottom: 10px; }
.cert-sub { font-size: 1.25rem; color: #059669; font-weight: 700; margin-bottom: 40px; }
.cert-body { font-size: 1.2rem; color: #4b5563; line-height: 1.8; margin-bottom: 40px; }
.cert-name { font-family: 'Cinzel', serif; font-size: 2.5rem; color: #059669; margin: 20px 0; border-bottom: 2px solid #e5e7eb; display: inline-block; padding: 0 40px; }
.cert-name { font-family: 'Amiri', serif; font-size: 2.5rem; color: #059669; margin: 20px 0; border-bottom: 2px solid #e5e7eb; display: inline-block; padding: 0 40px; }
.cert-footer { margin-top: 60px; display: flex; justify-content: space-between; align-items: flex-end; }
.signature { border-top: 1px solid #9ca3af; padding-top: 10px; width: 200px; font-style: italic; color: #6b7280; }
.stamp { width: 120px; height: 120px; border: 4px double #059669; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #059669; font-weight: 800; transform: rotate(-15deg); font-size: 0.8rem; margin: 0 auto; }
.signature { border-top: 1px solid #9ca3af; padding-top: 10px; width: 200px; font-weight: 600; color: #6b7280; }
.stamp { width: 120px; height: 120px; border: 4px double #059669; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #059669; font-weight: 800; transform: rotate(-15deg); font-size: 0.8rem; margin: 0 auto; text-align: center; line-height: 1.4; }
@media print {
body { background: #fff; padding: 0; }
@ -64,43 +67,46 @@ $orgName = $org['name_en'] ?? 'Organization';
}
.no-print { margin-top: 30px; text-align: center; }
.btn-print { background: #059669; color: #fff; border: none; padding: 12px 30px; border-radius: 9999px; font-weight: 600; cursor: pointer; text-decoration: none; }
.btn-print { background: #059669; color: #fff; border: none; padding: 12px 30px; border-radius: 9999px; font-weight: 600; cursor: pointer; text-decoration: none; font-family: 'Cairo', sans-serif; }
</style>
</head>
<body>
<div class="certificate-container">
<div class="cert-header">Certificate</div>
<div class="cert-sub">of Appreciation</div>
<div class="cert-logo">
<img src="assets/images/logo_1770967720.jpg" alt="Logo">
</div>
<div class="cert-header">شهادة تقدير</div>
<div class="cert-sub">شكر وعرفان</div>
<div class="cert-body">
This certificate is proudly presented to
يسرنا تقديم هذه الشهادة إلى
<br>
<div class="cert-name"><?= htmlspecialchars($don['donor_name'] ?: 'Valued Donor') ?></div>
<div class="cert-name"><?= htmlspecialchars($don['donor_name'] ?: 'فاعل خير') ?></div>
<br>
In recognition of their generous donation of <strong>OMR <?= number_format($don['amount'], 3) ?></strong>
تقديراً لتبرعكم الكريم بمبلغ <strong><?= number_format($don['amount'], 3) ?> ر.ع.</strong>
<br>
towards the cause: <strong><?= htmlspecialchars($don['case_title']) ?></strong>
لدعم حالة: <strong><?= htmlspecialchars($don['case_title_ar'] ?: $don['case_title']) ?></strong>
</div>
<div class="stamp">
OFFICIAL<br>SEAL<br><?= strtoupper(htmlspecialchars($orgName)) ?>
الختم<br>الرسمي<br><?= htmlspecialchars($orgName) ?>
</div>
<div class="cert-footer">
<div class="signature">
Date: <?= date('M j, Y', strtotime($don['created_at'])) ?>
التاريخ: <?= date('Y/m/d', strtotime($don['created_at'])) ?>
</div>
<div class="signature">
Authorized Signature<br>
التوقيع المعتمد<br>
<?= htmlspecialchars($orgName) ?>
</div>
</div>
</div>
<div class="no-print">
<button onclick="window.print()" class="btn-print">Print or Save as PDF</button>
<a href="index.php" style="margin-left: 15px; color: #6b7280; text-decoration: none;">Back to Home</a>
<button onclick="window.print()" class="btn-print">طباعة أو حفظ كملف PDF</button>
<a href="index.php" style="margin-right: 15px; color: #6b7280; text-decoration: none;">العودة للرئيسية</a>
</div>
</body>

View File

@ -12,6 +12,7 @@ $amount = (float)$_POST['amount'];
$donor_name = $_POST['donor_name'] ?? 'Anonymous';
$donor_email = $_POST['donor_email'] ?? '';
$donor_phone = $_POST['donor_phone'] ?? '';
$lang = $_POST['lang'] ?? 'ar';
// Gift fields
$is_gift = (int)($_POST['is_gift'] ?? 0);
@ -30,8 +31,8 @@ $stmt = $pdo->prepare("SELECT * FROM cases WHERE id = ?");
$stmt->execute([$case_id]);
$case = $stmt->fetch();
if (!$case) {
die("Case not found");
if (!$case || $case['status'] !== 'active') {
die("Case not found or not active");
}
// Create pending donation
@ -52,8 +53,8 @@ $payload = [
'quantity' => 1
]
],
'success_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/index.php',
'success_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/success.php?session_id={CHECKOUT_SESSION_ID}&lang=' . $lang,
'cancel_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/index.php?lang=' . $lang,
'metadata' => [
'donation_id' => $donation_id,
'case_id' => $case_id
@ -65,7 +66,7 @@ if (THAWANI_SECRET_KEY === 'rRQ26GcsZ60u9Y9v9876543210' || empty(THAWANI_SECRET_
// Simulation Mode
?>
<!DOCTYPE html>
<html>
<html lang="<?= htmlspecialchars($lang) ?>">
<head><title>Simulating Thawani Checkout</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"></head>
<body class="bg-light p-5 text-center">
<div class="card mx-auto" style="max-width: 500px;">
@ -81,9 +82,10 @@ if (THAWANI_SECRET_KEY === 'rRQ26GcsZ60u9Y9v9876543210' || empty(THAWANI_SECRET_
<form action="success.php" method="GET">
<input type="hidden" name="session_id" value="mock_session_<?= time() ?>">
<input type="hidden" name="donation_id" value="<?= $donation_id ?>">
<input type="hidden" name="lang" value="<?= htmlspecialchars($lang) ?>">
<button type="submit" class="btn btn-success w-100">Simulate Success Payment</button>
</form>
<a href="index.php" class="btn btn-link mt-2">Cancel</a>
<a href="index.php?lang=<?= htmlspecialchars($lang) ?>" class="btn btn-link mt-2">Cancel</a>
</div>
</div>
</body>
@ -129,5 +131,5 @@ if (isset($data['success']) && $data['success'] === true && isset($data['data'][
echo "<pre>";
print_r($data);
echo "</pre>";
echo "<a href='index.php'>Go Back</a>";
}
echo "<a href='index.php?lang=" . htmlspecialchars($lang) . "'>Go Back</a>";
}

174
includes/I18N/Arabic.php Normal file
View File

@ -0,0 +1,174 @@
<?php
namespace I18N;
class Arabic {
private $presentation_forms = [
'0621' => ['FE80', 'FE80', 'FE80', 'FE80'], // Hamza
'0622' => ['FE81', 'FE82', 'FE81', 'FE82'], // Alef with Madda
'0623' => ['FE83', 'FE84', 'FE83', 'FE84'], // Alef with Hamza Above
'0624' => ['FE85', 'FE86', 'FE85', 'FE86'], // Waw with Hamza Above
'0625' => ['FE87', 'FE88', 'FE87', 'FE88'], // Alef with Hamza Below
'0626' => ['FE89', 'FE8A', 'FE8B', 'FE8C'], // Yeh with Hamza Above
'0627' => ['FE8D', 'FE8E', 'FE8D', 'FE8E'], // Alef
'0628' => ['FE8F', 'FE90', 'FE91', 'FE92'], // Beh
'0629' => ['FE93', 'FE94', 'FE93', 'FE94'], // Teh Marbuta
'062A' => ['FE95', 'FE96', 'FE97', 'FE98'], // Teh
'062B' => ['FE99', 'FE9A', 'FE9B', 'FE9C'], // Theh
'062C' => ['FE9D', 'FE9E', 'FE9F', 'FEA0'], // Jeem
'062D' => ['FEA1', 'FEA2', 'FEA3', 'FEA4'], // Hah
'062E' => ['FEA5', 'FEA6', 'FEA7', 'FEA8'], // Khah
'062F' => ['FEA9', 'FEAA', 'FEA9', 'FEAA'], // Dal
'0630' => ['FEAB', 'FEAC', 'FEAB', 'FEAC'], // Thal
'0631' => ['FEAD', 'FEAE', 'FEAD', 'FEAE'], // Reh
'0632' => ['FEAF', 'FEB0', 'FEAF', 'FEB0'], // Zain
'0633' => ['FEB1', 'FEB2', 'FEB3', 'FEB4'], // Seen
'0634' => ['FEB5', 'FEB6', 'FEB7', 'FEB8'], // Sheen
'0635' => ['FEB9', 'FEBA', 'FEBB', 'FEBC'], // Sad
'0636' => ['FEBD', 'FEBE', 'FEBF', 'FEC0'], // Dad
'0637' => ['FEC1', 'FEC2', 'FEC3', 'FEC4'], // Tah
'0638' => ['FEC5', 'FEC6', 'FEC7', 'FEC8'], // Zah
'0639' => ['FEC9', 'FECA', 'FECB', 'FECC'], // Ain
'063A' => ['FECD', 'FECE', 'FECF', 'FED0'], // Ghain
'0640' => ['0640', '0640', '0640', '0640'], // Tatweel
'0641' => ['FED1', 'FED2', 'FED3', 'FED4'], // Feh
'0642' => ['FED5', 'FED6', 'FED7', 'FED8'], // Qaf
'0643' => ['FED9', 'FEDA', 'FEDB', 'FEDC'], // Kaf
'0644' => ['FEDD', 'FEDE', 'FEDF', 'FEE0'], // Lam
'0645' => ['FEE1', 'FEE2', 'FEE3', 'FEE4'], // Meem
'0646' => ['FEE5', 'FEE6', 'FEE7', 'FEE8'], // Noon
'0647' => ['FEE9', 'FEEA', 'FEEB', 'FEEC'], // Heh
'0648' => ['FEED', 'FEEE', 'FEED', 'FEEE'], // Waw
'0649' => ['FEEF', 'FEF0', 'FEEF', 'FEF0'], // Alef Maksura
'064A' => ['FEF1', 'FEF2', 'FEF3', 'FEF4'], // Yeh
];
private $connects_before = [
'0626','0628','0629','062A','062B','062C','062D','062E',
'0633','0634','0635','0636','0637','0638','0639','063A','0640','0641','0642',
'0643','0644','0645','0646','0647','064A'
];
// Letters that prevent the *next* letter from connecting to *this* one (Right-joining only)
private $disconnects_after = [
'0621','0622','0623','0624','0625','0627','062F','0630','0631','0632','0648','0649'
];
public function utf8Glyphs($str) {
$hex = $this->utf8ToHexArray($str);
$res = [];
$len = count($hex);
for ($i = 0; $i < $len; $i++) {
$current = $hex[$i];
// Skip non-Arabic chars (or spaces) in shaping logic
if (!isset($this->presentation_forms[$current])) {
$res[] = $current;
continue;
}
// Check for Lam-Alef Ligature
if ($current == '0644' && $i < $len - 1) {
$next = $hex[$i+1];
$ligature = null;
switch($next) {
case '0622': $ligature = ['FEF5', 'FEF6', 'FEF5', 'FEF6']; break; // Lam-Alef Madda
case '0623': $ligature = ['FEF7', 'FEF8', 'FEF7', 'FEF8']; break; // Lam-Alef Hamza Above
case '0625': $ligature = ['FEF9', 'FEFA', 'FEF9', 'FEFA']; break; // Lam-Alef Hamza Below
case '0627': $ligature = ['FEFB', 'FEFC', 'FEFB', 'FEFC']; break; // Lam-Alef
}
if ($ligature) {
// Determine connection from previous
$prev = $i > 0 ? $hex[$i-1] : null;
$connect_prev = $prev && $this->canConnect($prev);
if ($connect_prev) {
$res[] = $ligature[1]; // Final form of ligature
} else {
$res[] = $ligature[0]; // Isolated form of ligature
}
$i++; // Skip the Alef
continue;
}
}
// Normal Logic
$prev = $i > 0 ? $hex[$i-1] : null;
$next = $i < $len - 1 ? $hex[$i+1] : null;
$connect_prev = $prev && $this->canConnect($prev);
// For next connection, we check if current can connect left AND next can connect right
$connect_next = $next && $this->canConnectLeft($current) && $this->isArabic($next);
// Refined Logic
if ($connect_prev && $connect_next) {
$form = 3; // Medial
} elseif ($connect_prev) {
$form = 1; // Final
} elseif ($connect_next) {
$form = 2; // Initial
} else {
$form = 0; // Isolated
}
$res[] = $this->presentation_forms[$current][$form];
}
$s = $this->hexArrayToUtf8($res);
return $this->mb_strrev($s);
}
private function canConnect($hex) {
// Can $hex connect to the *next* letter?
// True if it is Arabic and NOT in disconnects_after
return isset($this->presentation_forms[$hex]) && !in_array($hex, $this->disconnects_after) && $hex != '0621'; // Hamza doesn't connect
}
private function canConnectLeft($hex) {
// Can $hex connect to the *next* letter? (Same as above)
return $this->canConnect($hex);
}
private function isArabic($hex) {
return isset($this->presentation_forms[$hex]);
}
private function utf8ToHexArray($str) {
$out = [];
$len = mb_strlen($str, 'UTF-8');
for($i=0; $i<$len; $i++) {
$c = mb_substr($str, $i, 1, 'UTF-8');
$val = $this->uniord($c);
$out[] = sprintf("%04X", $val);
}
return $out;
}
private function hexArrayToUtf8($arr) {
$str = '';
foreach ($arr as $hex) {
$val = hexdec($hex);
$str .= $this->unichr($val);
}
return $str;
}
private function uniord($u) {
if (strlen($u) === 1) return ord($u);
$k = mb_convert_encoding($u, 'UCS-2LE', 'UTF-8');
$k1 = ord(substr($k, 0, 1));
$k2 = ord(substr($k, 1, 1));
return $k2 * 256 + $k1;
}
private function unichr($u) {
return mb_convert_encoding(pack("n", $u), 'UTF-8', 'UCS-2BE');
}
private function mb_strrev($str){
preg_match_all('/./us', $str, $ar);
return join('', array_reverse($ar[0]));
}
}

View File

@ -0,0 +1 @@
404: Not Found

Binary file not shown.

View File

@ -0,0 +1,120 @@
<?php
$rangeid=58;
$prevcid=126;
$prevwidth=556;
$interval=false;
$range=array (
32 =>
array (
0 => 292,
1 => 235,
2 => 273,
3 => 468,
4 => 486,
5 => 651,
6 => 666,
7 => 156,
),
40 =>
array (
0 => 458,
1 => 458,
'interval' => true,
),
42 =>
array (
0 => 432,
1 => 529,
2 => 196,
3 => 368,
4 => 317,
5 => 344,
),
48 =>
array (
0 => 532,
1 => 532,
'interval' => true,
2 => 532,
3 => 532,
4 => 532,
5 => 532,
6 => 532,
7 => 532,
8 => 532,
9 => 532,
),
58 =>
array (
0 => 220,
1 => 226,
2 => 480,
3 => 551,
4 => 481,
5 => 347,
6 => 730,
7 => 612,
8 => 582,
9 => 658,
10 => 693,
11 => 573,
12 => 522,
13 => 703,
14 => 708,
15 => 316,
16 => 305,
17 => 641,
18 => 550,
19 => 885,
20 => 757,
21 => 730,
22 => 535,
23 => 729,
24 => 606,
25 => 490,
26 => 611,
27 => 706,
28 => 623,
29 => 878,
30 => 652,
31 => 583,
32 => 618,
33 => 361,
34 => 344,
35 => 361,
36 => 380,
37 => 493,
38 => 333,
39 => 420,
40 => 486,
41 => 413,
42 => 502,
43 => 419,
44 => 300,
45 => 455,
46 => 503,
47 => 263,
48 => 233,
49 => 505,
50 => 249,
51 => 771,
52 => 519,
53 => 497,
54 => 498,
55 => 485,
56 => 374,
57 => 360,
58 => 303,
59 => 502,
60 => 461,
61 => 704,
62 => 464,
63 => 458,
64 => 436,
65 => 397,
66 => 200,
67 => 397,
68 => 556,
),
);
?>

View File

@ -0,0 +1,19 @@
<?php
$name='Amiri-Regular';
$type='TTF';
$desc=array (
'Ascent' => 1124.0,
'Descent' => -634.0,
'CapHeight' => 646.0,
'Flags' => 4,
'FontBBox' => '[-438 -900 11467 1815]',
'ItalicAngle' => 0.0,
'StemV' => 87.0,
'MissingWidth' => 364.0,
);
$up=-512;
$ut=50;
$ttffile='/home/ubuntu/executor/workspace/includes/fpdf/font/unifont/amiri.ttf';
$originalsize=431116;
$fontkey='amiri';
?>

File diff suppressed because it is too large Load Diff

View File

@ -1,447 +1 @@
<?php
/*******************************************************************************
* Utility to generate font definition files *
* *
* Version: 1.31 *
* Date: 2019-12-07 *
* Author: Olivier PLATHEY *
*******************************************************************************/
require('ttfparser.php');
function Message($txt, $severity='')
{
if(PHP_SAPI=='cli')
{
if($severity)
echo "$severity: ";
echo "$txt\n";
}
else
{
if($severity)
echo "<b>$severity</b>: ";
echo "$txt<br>";
}
}
function Notice($txt)
{
Message($txt, 'Notice');
}
function Warning($txt)
{
Message($txt, 'Warning');
}
function Error($txt)
{
Message($txt, 'Error');
exit;
}
function LoadMap($enc)
{
$file = dirname(__FILE__).'/'.strtolower($enc).'.map';
$a = file($file);
if(empty($a))
Error('Encoding not found: '.$enc);
$map = array_fill(0, 256, array('uv'=>-1, 'name'=>'.notdef'));
foreach($a as $line)
{
$e = explode(' ', rtrim($line));
$c = hexdec(substr($e[0],1));
$uv = hexdec(substr($e[1],2));
$name = $e[2];
$map[$c] = array('uv'=>$uv, 'name'=>$name);
}
return $map;
}
function GetInfoFromTrueType($file, $embed, $subset, $map)
{
// Return information from a TrueType font
try
{
$ttf = new TTFParser($file);
$ttf->Parse();
}
catch(Exception $e)
{
Error($e->getMessage());
}
if($embed)
{
if(!$ttf->embeddable)
Error('Font license does not allow embedding');
if($subset)
{
$chars = array();
foreach($map as $v)
{
if($v['name']!='.notdef')
$chars[] = $v['uv'];
}
$ttf->Subset($chars);
$info['Data'] = $ttf->Build();
}
else
$info['Data'] = file_get_contents($file);
$info['OriginalSize'] = strlen($info['Data']);
}
$k = 1000/$ttf->unitsPerEm;
$info['FontName'] = $ttf->postScriptName;
$info['Bold'] = $ttf->bold;
$info['ItalicAngle'] = $ttf->italicAngle;
$info['IsFixedPitch'] = $ttf->isFixedPitch;
$info['Ascender'] = round($k*$ttf->typoAscender);
$info['Descender'] = round($k*$ttf->typoDescender);
$info['UnderlineThickness'] = round($k*$ttf->underlineThickness);
$info['UnderlinePosition'] = round($k*$ttf->underlinePosition);
$info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax));
$info['CapHeight'] = round($k*$ttf->capHeight);
$info['MissingWidth'] = round($k*$ttf->glyphs[0]['w']);
$widths = array_fill(0, 256, $info['MissingWidth']);
foreach($map as $c=>$v)
{
if($v['name']!='.notdef')
{
if(isset($ttf->chars[$v['uv']]))
{
$id = $ttf->chars[$v['uv']];
$w = $ttf->glyphs[$id]['w'];
$widths[$c] = round($k*$w);
}
else
Warning('Character '.$v['name'].' is missing');
}
}
$info['Widths'] = $widths;
return $info;
}
function GetInfoFromType1($file, $embed, $map)
{
// Return information from a Type1 font
if($embed)
{
$f = fopen($file, 'rb');
if(!$f)
Error('Can\'t open font file');
// Read first segment
$a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
if($a['marker']!=128)
Error('Font file is not a valid binary Type1');
$size1 = $a['size'];
$data = fread($f, $size1);
// Read second segment
$a = unpack('Cmarker/Ctype/Vsize', fread($f,6));
if($a['marker']!=128)
Error('Font file is not a valid binary Type1');
$size2 = $a['size'];
$data .= fread($f, $size2);
fclose($f);
$info['Data'] = $data;
$info['Size1'] = $size1;
$info['Size2'] = $size2;
}
$afm = substr($file, 0, -3).'afm';
if(!file_exists($afm))
Error('AFM font file not found: '.$afm);
$a = file($afm);
if(empty($a))
Error('AFM file empty or not readable');
foreach($a as $line)
{
$e = explode(' ', rtrim($line));
if(count($e)<2)
continue;
$entry = $e[0];
if($entry=='C')
{
$w = $e[4];
$name = $e[7];
$cw[$name] = $w;
}
elseif($entry=='FontName')
$info['FontName'] = $e[1];
elseif($entry=='Weight')
$info['Weight'] = $e[1];
elseif($entry=='ItalicAngle')
$info['ItalicAngle'] = (int)$e[1];
elseif($entry=='Ascender')
$info['Ascender'] = (int)$e[1];
elseif($entry=='Descender')
$info['Descender'] = (int)$e[1];
elseif($entry=='UnderlineThickness')
$info['UnderlineThickness'] = (int)$e[1];
elseif($entry=='UnderlinePosition')
$info['UnderlinePosition'] = (int)$e[1];
elseif($entry=='IsFixedPitch')
$info['IsFixedPitch'] = ($e[1]=='true');
elseif($entry=='FontBBox')
$info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]);
elseif($entry=='CapHeight')
$info['CapHeight'] = (int)$e[1];
elseif($entry=='StdVW')
$info['StdVW'] = (int)$e[1];
}
if(!isset($info['FontName']))
Error('FontName missing in AFM file');
if(!isset($info['Ascender']))
$info['Ascender'] = $info['FontBBox'][3];
if(!isset($info['Descender']))
$info['Descender'] = $info['FontBBox'][1];
$info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']);
if(isset($cw['.notdef']))
$info['MissingWidth'] = $cw['.notdef'];
else
$info['MissingWidth'] = 0;
$widths = array_fill(0, 256, $info['MissingWidth']);
foreach($map as $c=>$v)
{
if($v['name']!='.notdef')
{
if(isset($cw[$v['name']]))
$widths[$c] = $cw[$v['name']];
else
Warning('Character '.$v['name'].' is missing');
}
}
$info['Widths'] = $widths;
return $info;
}
function MakeFontDescriptor($info)
{
// Ascent
$fd = "array('Ascent'=>".$info['Ascender'];
// Descent
$fd .= ",'Descent'=>".$info['Descender'];
// CapHeight
if(!empty($info['CapHeight']))
$fd .= ",'CapHeight'=>".$info['CapHeight'];
else
$fd .= ",'CapHeight'=>".$info['Ascender'];
// Flags
$flags = 0;
if($info['IsFixedPitch'])
$flags += 1<<0;
$flags += 1<<5;
if($info['ItalicAngle']!=0)
$flags += 1<<6;
$fd .= ",'Flags'=>".$flags;
// FontBBox
$fbb = $info['FontBBox'];
$fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
// ItalicAngle
$fd .= ",'ItalicAngle'=>".$info['ItalicAngle'];
// StemV
if(isset($info['StdVW']))
$stemv = $info['StdVW'];
elseif($info['Bold'])
$stemv = 120;
else
$stemv = 70;
$fd .= ",'StemV'=>".$stemv;
// MissingWidth
$fd .= ",'MissingWidth'=>".$info['MissingWidth'].')';
return $fd;
}
function MakeWidthArray($widths)
{
$s = "array(\n\t";
for($c=0;$c<=255;$c++)
{
if(chr($c)=="'")
$s .= "'\\''";
elseif(chr($c)=="\\")
$s .= "'\\\\'";
elseif($c>=32 && $c<=126)
$s .= "'".chr($c)."'";
else
$s .= "chr($c)";
$s .= '=>'.$widths[$c];
if($c<255)
$s .= ',';
if(($c+1)%22==0)
$s .= "\n\t";
}
$s .= ')';
return $s;
}
function MakeFontEncoding($map)
{
// Build differences from reference encoding
$ref = LoadMap('cp1252');
$s = '';
$last = 0;
for($c=32;$c<=255;$c++)
{
if($map[$c]['name']!=$ref[$c]['name'])
{
if($c!=$last+1)
$s .= $c.' ';
$last = $c;
$s .= '/'.$map[$c]['name'].' ';
}
}
return rtrim($s);
}
function MakeUnicodeArray($map)
{
// Build mapping to Unicode values
$ranges = array();
foreach($map as $c=>$v)
{
$uv = $v['uv'];
if($uv!=-1)
{
if(isset($range))
{
if($c==$range[1]+1 && $uv==$range[3]+1)
{
$range[1]++;
$range[3]++;
}
else
{
$ranges[] = $range;
$range = array($c, $c, $uv, $uv);
}
}
else
$range = array($c, $c, $uv, $uv);
}
}
$ranges[] = $range;
foreach($ranges as $range)
{
if(isset($s))
$s .= ',';
else
$s = 'array(';
$s .= $range[0].'=>';
$nb = $range[1]-$range[0]+1;
if($nb>1)
$s .= 'array('.$range[2].','.$nb.')';
else
$s .= $range[2];
}
$s .= ')';
return $s;
}
function SaveToFile($file, $s, $mode)
{
$f = fopen($file, 'w'.$mode);
if(!$f)
Error('Can\'t write to file '.$file);
fwrite($f, $s);
fclose($f);
}
function MakeDefinitionFile($file, $type, $enc, $embed, $subset, $map, $info)
{
$s = "<?php\n";
$s .= '$type = \''.$type."';\n";
$s .= '$name = \''.$info['FontName']."';\n";
$s .= '$desc = '.MakeFontDescriptor($info).";\n";
$s .= '$up = '.$info['UnderlinePosition'].";\n";
$s .= '$ut = '.$info['UnderlineThickness'].";\n";
$s .= '$cw = '.MakeWidthArray($info['Widths']).";\n";
$s .= '$enc = \''.$enc."';\n";
$diff = MakeFontEncoding($map);
if($diff)
$s .= '$diff = \''.$diff."';\n";
$s .= '$uv = '.MakeUnicodeArray($map).";\n";
if($embed)
{
$s .= '$file = \''.$info['File']."';\n";
if($type=='Type1')
{
$s .= '$size1 = '.$info['Size1'].";\n";
$s .= '$size2 = '.$info['Size2'].";\n";
}
else
{
$s .= '$originalsize = '.$info['OriginalSize'].";\n";
if($subset)
$s .= "\$subsetted = true;\n";
}
}
$s .= "?>\n";
SaveToFile($file, $s, 't');
}
function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=true)
{
// Generate a font definition file
if(!file_exists($fontfile))
Error('Font file not found: '.$fontfile);
$ext = strtolower(substr($fontfile,-3));
if($ext=='ttf' || $ext=='otf')
$type = 'TrueType';
elseif($ext=='pfb')
$type = 'Type1';
else
Error('Unrecognized font file extension: '.$ext);
$map = LoadMap($enc);
if($type=='TrueType')
$info = GetInfoFromTrueType($fontfile, $embed, $subset, $map);
else
$info = GetInfoFromType1($fontfile, $embed, $map);
$basename = substr(basename($fontfile), 0, -4);
if($embed)
{
if(function_exists('gzcompress'))
{
$file = $basename.'.z';
SaveToFile($file, gzcompress($info['Data']), 'b');
$info['File'] = $file;
Message('Font file compressed: '.$file);
}
else
{
$info['File'] = basename($fontfile);
$subset = false;
Notice('Font file could not be compressed (zlib extension not available)');
}
}
MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $subset, $map, $info);
Message('Font definition file generated: '.$basename.'.php');
}
if(PHP_SAPI=='cli')
{
// Command-line interface
ini_set('log_errors', '0');
if($argc==1)
die("Usage: php makefont.php fontfile [encoding] [embed] [subset]\n");
$fontfile = $argv[1];
if($argc>=3)
$enc = $argv[2];
else
$enc = 'cp1252';
if($argc>=4)
$embed = ($argv[3]=='true' || $argv[3]=='1');
else
$embed = true;
if($argc>=5)
$subset = ($argv[4]=='true' || $argv[4]=='1');
else
$subset = true;
MakeFont($fontfile, $enc, $embed, $subset);
}
?>
404: Not Found

View File

@ -1,714 +1 @@
<?php
/*******************************************************************************
* Class to parse and subset TrueType fonts *
* *
* Version: 1.11 *
* Date: 2021-04-18 *
* Author: Olivier PLATHEY *
*******************************************************************************/
class TTFParser
{
protected $f;
protected $tables;
protected $numberOfHMetrics;
protected $numGlyphs;
protected $glyphNames;
protected $indexToLocFormat;
protected $subsettedChars;
protected $subsettedGlyphs;
public $chars;
public $glyphs;
public $unitsPerEm;
public $xMin, $yMin, $xMax, $yMax;
public $postScriptName;
public $embeddable;
public $bold;
public $typoAscender;
public $typoDescender;
public $capHeight;
public $italicAngle;
public $underlinePosition;
public $underlineThickness;
public $isFixedPitch;
function __construct($file)
{
$this->f = fopen($file, 'rb');
if(!$this->f)
$this->Error('Can\'t open file: '.$file);
}
function __destruct()
{
if(is_resource($this->f))
fclose($this->f);
}
function Parse()
{
$this->ParseOffsetTable();
$this->ParseHead();
$this->ParseHhea();
$this->ParseMaxp();
$this->ParseHmtx();
$this->ParseLoca();
$this->ParseGlyf();
$this->ParseCmap();
$this->ParseName();
$this->ParseOS2();
$this->ParsePost();
}
function ParseOffsetTable()
{
$version = $this->Read(4);
if($version=='OTTO')
$this->Error('OpenType fonts based on PostScript outlines are not supported');
if($version!="\x00\x01\x00\x00")
$this->Error('Unrecognized file format');
$numTables = $this->ReadUShort();
$this->Skip(3*2); // searchRange, entrySelector, rangeShift
$this->tables = array();
for($i=0;$i<$numTables;$i++)
{
$tag = $this->Read(4);
$checkSum = $this->Read(4);
$offset = $this->ReadULong();
$length = $this->ReadULong();
$this->tables[$tag] = array('offset'=>$offset, 'length'=>$length, 'checkSum'=>$checkSum);
}
}
function ParseHead()
{
$this->Seek('head');
$this->Skip(3*4); // version, fontRevision, checkSumAdjustment
$magicNumber = $this->ReadULong();
if($magicNumber!=0x5F0F3CF5)
$this->Error('Incorrect magic number');
$this->Skip(2); // flags
$this->unitsPerEm = $this->ReadUShort();
$this->Skip(2*8); // created, modified
$this->xMin = $this->ReadShort();
$this->yMin = $this->ReadShort();
$this->xMax = $this->ReadShort();
$this->yMax = $this->ReadShort();
$this->Skip(3*2); // macStyle, lowestRecPPEM, fontDirectionHint
$this->indexToLocFormat = $this->ReadShort();
}
function ParseHhea()
{
$this->Seek('hhea');
$this->Skip(4+15*2);
$this->numberOfHMetrics = $this->ReadUShort();
}
function ParseMaxp()
{
$this->Seek('maxp');
$this->Skip(4);
$this->numGlyphs = $this->ReadUShort();
}
function ParseHmtx()
{
$this->Seek('hmtx');
$this->glyphs = array();
for($i=0;$i<$this->numberOfHMetrics;$i++)
{
$advanceWidth = $this->ReadUShort();
$lsb = $this->ReadShort();
$this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb);
}
for($i=$this->numberOfHMetrics;$i<$this->numGlyphs;$i++)
{
$lsb = $this->ReadShort();
$this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb);
}
}
function ParseLoca()
{
$this->Seek('loca');
$offsets = array();
if($this->indexToLocFormat==0)
{
// Short format
for($i=0;$i<=$this->numGlyphs;$i++)
$offsets[] = 2*$this->ReadUShort();
}
else
{
// Long format
for($i=0;$i<=$this->numGlyphs;$i++)
$offsets[] = $this->ReadULong();
}
for($i=0;$i<$this->numGlyphs;$i++)
{
$this->glyphs[$i]['offset'] = $offsets[$i];
$this->glyphs[$i]['length'] = $offsets[$i+1] - $offsets[$i];
}
}
function ParseGlyf()
{
$tableOffset = $this->tables['glyf']['offset'];
foreach($this->glyphs as &$glyph)
{
if($glyph['length']>0)
{
fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET);
if($this->ReadShort()<0)
{
// Composite glyph
$this->Skip(4*2); // xMin, yMin, xMax, yMax
$offset = 5*2;
$a = array();
do
{
$flags = $this->ReadUShort();
$index = $this->ReadUShort();
$a[$offset+2] = $index;
if($flags & 1) // ARG_1_AND_2_ARE_WORDS
$skip = 2*2;
else
$skip = 2;
if($flags & 8) // WE_HAVE_A_SCALE
$skip += 2;
elseif($flags & 64) // WE_HAVE_AN_X_AND_Y_SCALE
$skip += 2*2;
elseif($flags & 128) // WE_HAVE_A_TWO_BY_TWO
$skip += 4*2;
$this->Skip($skip);
$offset += 2*2 + $skip;
}
while($flags & 32); // MORE_COMPONENTS
$glyph['components'] = $a;
}
}
}
}
function ParseCmap()
{
$this->Seek('cmap');
$this->Skip(2); // version
$numTables = $this->ReadUShort();
$offset31 = 0;
for($i=0;$i<$numTables;$i++)
{
$platformID = $this->ReadUShort();
$encodingID = $this->ReadUShort();
$offset = $this->ReadULong();
if($platformID==3 && $encodingID==1)
$offset31 = $offset;
}
if($offset31==0)
$this->Error('No Unicode encoding found');
$startCount = array();
$endCount = array();
$idDelta = array();
$idRangeOffset = array();
$this->chars = array();
fseek($this->f, $this->tables['cmap']['offset']+$offset31, SEEK_SET);
$format = $this->ReadUShort();
if($format!=4)
$this->Error('Unexpected subtable format: '.$format);
$this->Skip(2*2); // length, language
$segCount = $this->ReadUShort()/2;
$this->Skip(3*2); // searchRange, entrySelector, rangeShift
for($i=0;$i<$segCount;$i++)
$endCount[$i] = $this->ReadUShort();
$this->Skip(2); // reservedPad
for($i=0;$i<$segCount;$i++)
$startCount[$i] = $this->ReadUShort();
for($i=0;$i<$segCount;$i++)
$idDelta[$i] = $this->ReadShort();
$offset = ftell($this->f);
for($i=0;$i<$segCount;$i++)
$idRangeOffset[$i] = $this->ReadUShort();
for($i=0;$i<$segCount;$i++)
{
$c1 = $startCount[$i];
$c2 = $endCount[$i];
$d = $idDelta[$i];
$ro = $idRangeOffset[$i];
if($ro>0)
fseek($this->f, $offset+2*$i+$ro, SEEK_SET);
for($c=$c1;$c<=$c2;$c++)
{
if($c==0xFFFF)
break;
if($ro>0)
{
$gid = $this->ReadUShort();
if($gid>0)
$gid += $d;
}
else
$gid = $c+$d;
if($gid>=65536)
$gid -= 65536;
if($gid>0)
$this->chars[$c] = $gid;
}
}
}
function ParseName()
{
$this->Seek('name');
$tableOffset = $this->tables['name']['offset'];
$this->postScriptName = '';
$this->Skip(2); // format
$count = $this->ReadUShort();
$stringOffset = $this->ReadUShort();
for($i=0;$i<$count;$i++)
{
$this->Skip(3*2); // platformID, encodingID, languageID
$nameID = $this->ReadUShort();
$length = $this->ReadUShort();
$offset = $this->ReadUShort();
if($nameID==6)
{
// PostScript name
fseek($this->f, $tableOffset+$stringOffset+$offset, SEEK_SET);
$s = $this->Read($length);
$s = str_replace(chr(0), '', $s);
$s = preg_replace('|[ \[\](){}<>/%]|', '', $s);
$this->postScriptName = $s;
break;
}
}
if($this->postScriptName=='')
$this->Error('PostScript name not found');
}
function ParseOS2()
{
$this->Seek('OS/2');
$version = $this->ReadUShort();
$this->Skip(3*2); // xAvgCharWidth, usWeightClass, usWidthClass
$fsType = $this->ReadUShort();
$this->embeddable = ($fsType!=2) && ($fsType & 0x200)==0;
$this->Skip(11*2+10+4*4+4);
$fsSelection = $this->ReadUShort();
$this->bold = ($fsSelection & 32)!=0;
$this->Skip(2*2); // usFirstCharIndex, usLastCharIndex
$this->typoAscender = $this->ReadShort();
$this->typoDescender = $this->ReadShort();
if($version>=2)
{
$this->Skip(3*2+2*4+2);
$this->capHeight = $this->ReadShort();
}
else
$this->capHeight = 0;
}
function ParsePost()
{
$this->Seek('post');
$version = $this->ReadULong();
$this->italicAngle = $this->ReadShort();
$this->Skip(2); // Skip decimal part
$this->underlinePosition = $this->ReadShort();
$this->underlineThickness = $this->ReadShort();
$this->isFixedPitch = ($this->ReadULong()!=0);
if($version==0x20000)
{
// Extract glyph names
$this->Skip(4*4); // min/max usage
$this->Skip(2); // numberOfGlyphs
$glyphNameIndex = array();
$names = array();
$numNames = 0;
for($i=0;$i<$this->numGlyphs;$i++)
{
$index = $this->ReadUShort();
$glyphNameIndex[] = $index;
if($index>=258 && $index-257>$numNames)
$numNames = $index-257;
}
for($i=0;$i<$numNames;$i++)
{
$len = ord($this->Read(1));
$names[] = $this->Read($len);
}
foreach($glyphNameIndex as $i=>$index)
{
if($index>=258)
$this->glyphs[$i]['name'] = $names[$index-258];
else
$this->glyphs[$i]['name'] = $index;
}
$this->glyphNames = true;
}
else
$this->glyphNames = false;
}
function Subset($chars)
{
$this->subsettedGlyphs = array();
$this->AddGlyph(0);
$this->subsettedChars = array();
foreach($chars as $char)
{
if(isset($this->chars[$char]))
{
$this->subsettedChars[] = $char;
$this->AddGlyph($this->chars[$char]);
}
}
}
function AddGlyph($id)
{
if(!isset($this->glyphs[$id]['ssid']))
{
$this->glyphs[$id]['ssid'] = count($this->subsettedGlyphs);
$this->subsettedGlyphs[] = $id;
if(isset($this->glyphs[$id]['components']))
{
foreach($this->glyphs[$id]['components'] as $cid)
$this->AddGlyph($cid);
}
}
}
function Build()
{
$this->BuildCmap();
$this->BuildHhea();
$this->BuildHmtx();
$this->BuildLoca();
$this->BuildGlyf();
$this->BuildMaxp();
$this->BuildPost();
return $this->BuildFont();
}
function BuildCmap()
{
if(!isset($this->subsettedChars))
return;
// Divide charset in contiguous segments
$chars = $this->subsettedChars;
sort($chars);
$segments = array();
$segment = array($chars[0], $chars[0]);
for($i=1;$i<count($chars);$i++)
{
if($chars[$i]>$segment[1]+1)
{
$segments[] = $segment;
$segment = array($chars[$i], $chars[$i]);
}
else
$segment[1]++;
}
$segments[] = $segment;
$segments[] = array(0xFFFF, 0xFFFF);
$segCount = count($segments);
// Build a Format 4 subtable
$startCount = array();
$endCount = array();
$idDelta = array();
$idRangeOffset = array();
$glyphIdArray = '';
for($i=0;$i<$segCount;$i++)
{
list($start, $end) = $segments[$i];
$startCount[] = $start;
$endCount[] = $end;
if($start!=$end)
{
// Segment with multiple chars
$idDelta[] = 0;
$idRangeOffset[] = strlen($glyphIdArray) + ($segCount-$i)*2;
for($c=$start;$c<=$end;$c++)
{
$ssid = $this->glyphs[$this->chars[$c]]['ssid'];
$glyphIdArray .= pack('n', $ssid);
}
}
else
{
// Segment with a single char
if($start<0xFFFF)
$ssid = $this->glyphs[$this->chars[$start]]['ssid'];
else
$ssid = 0;
$idDelta[] = $ssid - $start;
$idRangeOffset[] = 0;
}
}
$entrySelector = 0;
$n = $segCount;
while($n!=1)
{
$n = $n>>1;
$entrySelector++;
}
$searchRange = (1<<$entrySelector)*2;
$rangeShift = 2*$segCount - $searchRange;
$cmap = pack('nnnn', 2*$segCount, $searchRange, $entrySelector, $rangeShift);
foreach($endCount as $val)
$cmap .= pack('n', $val);
$cmap .= pack('n', 0); // reservedPad
foreach($startCount as $val)
$cmap .= pack('n', $val);
foreach($idDelta as $val)
$cmap .= pack('n', $val);
foreach($idRangeOffset as $val)
$cmap .= pack('n', $val);
$cmap .= $glyphIdArray;
$data = pack('nn', 0, 1); // version, numTables
$data .= pack('nnN', 3, 1, 12); // platformID, encodingID, offset
$data .= pack('nnn', 4, 6+strlen($cmap), 0); // format, length, language
$data .= $cmap;
$this->SetTable('cmap', $data);
}
function BuildHhea()
{
$this->LoadTable('hhea');
$numberOfHMetrics = count($this->subsettedGlyphs);
$data = substr_replace($this->tables['hhea']['data'], pack('n',$numberOfHMetrics), 4+15*2, 2);
$this->SetTable('hhea', $data);
}
function BuildHmtx()
{
$data = '';
foreach($this->subsettedGlyphs as $id)
{
$glyph = $this->glyphs[$id];
$data .= pack('nn', $glyph['w'], $glyph['lsb']);
}
$this->SetTable('hmtx', $data);
}
function BuildLoca()
{
$data = '';
$offset = 0;
foreach($this->subsettedGlyphs as $id)
{
if($this->indexToLocFormat==0)
$data .= pack('n', $offset/2);
else
$data .= pack('N', $offset);
$offset += $this->glyphs[$id]['length'];
}
if($this->indexToLocFormat==0)
$data .= pack('n', $offset/2);
else
$data .= pack('N', $offset);
$this->SetTable('loca', $data);
}
function BuildGlyf()
{
$tableOffset = $this->tables['glyf']['offset'];
$data = '';
foreach($this->subsettedGlyphs as $id)
{
$glyph = $this->glyphs[$id];
fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET);
$glyph_data = $this->Read($glyph['length']);
if(isset($glyph['components']))
{
// Composite glyph
foreach($glyph['components'] as $offset=>$cid)
{
$ssid = $this->glyphs[$cid]['ssid'];
$glyph_data = substr_replace($glyph_data, pack('n',$ssid), $offset, 2);
}
}
$data .= $glyph_data;
}
$this->SetTable('glyf', $data);
}
function BuildMaxp()
{
$this->LoadTable('maxp');
$numGlyphs = count($this->subsettedGlyphs);
$data = substr_replace($this->tables['maxp']['data'], pack('n',$numGlyphs), 4, 2);
$this->SetTable('maxp', $data);
}
function BuildPost()
{
$this->Seek('post');
if($this->glyphNames)
{
// Version 2.0
$numberOfGlyphs = count($this->subsettedGlyphs);
$numNames = 0;
$names = '';
$data = $this->Read(2*4+2*2+5*4);
$data .= pack('n', $numberOfGlyphs);
foreach($this->subsettedGlyphs as $id)
{
$name = $this->glyphs[$id]['name'];
if(is_string($name))
{
$data .= pack('n', 258+$numNames);
$names .= chr(strlen($name)).$name;
$numNames++;
}
else
$data .= pack('n', $name);
}
$data .= $names;
}
else
{
// Version 3.0
$this->Skip(4);
$data = "\x00\x03\x00\x00";
$data .= $this->Read(4+2*2+5*4);
}
$this->SetTable('post', $data);
}
function BuildFont()
{
$tags = array();
foreach(array('cmap', 'cvt ', 'fpgm', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'prep') as $tag)
{
if(isset($this->tables[$tag]))
$tags[] = $tag;
}
$numTables = count($tags);
$offset = 12 + 16*$numTables;
foreach($tags as $tag)
{
if(!isset($this->tables[$tag]['data']))
$this->LoadTable($tag);
$this->tables[$tag]['offset'] = $offset;
$offset += strlen($this->tables[$tag]['data']);
}
// Build offset table
$entrySelector = 0;
$n = $numTables;
while($n!=1)
{
$n = $n>>1;
$entrySelector++;
}
$searchRange = 16*(1<<$entrySelector);
$rangeShift = 16*$numTables - $searchRange;
$offsetTable = pack('nnnnnn', 1, 0, $numTables, $searchRange, $entrySelector, $rangeShift);
foreach($tags as $tag)
{
$table = $this->tables[$tag];
$offsetTable .= $tag.$table['checkSum'].pack('NN', $table['offset'], $table['length']);
}
// Compute checkSumAdjustment (0xB1B0AFBA - font checkSum)
$s = $this->CheckSum($offsetTable);
foreach($tags as $tag)
$s .= $this->tables[$tag]['checkSum'];
$a = unpack('n2', $this->CheckSum($s));
$high = 0xB1B0 + ($a[1]^0xFFFF);
$low = 0xAFBA + ($a[2]^0xFFFF) + 1;
$checkSumAdjustment = pack('nn', $high+($low>>16), $low);
$this->tables['head']['data'] = substr_replace($this->tables['head']['data'], $checkSumAdjustment, 8, 4);
$font = $offsetTable;
foreach($tags as $tag)
$font .= $this->tables[$tag]['data'];
return $font;
}
function LoadTable($tag)
{
$this->Seek($tag);
$length = $this->tables[$tag]['length'];
$n = $length % 4;
if($n>0)
$length += 4 - $n;
$this->tables[$tag]['data'] = $this->Read($length);
}
function SetTable($tag, $data)
{
$length = strlen($data);
$n = $length % 4;
if($n>0)
$data = str_pad($data, $length+4-$n, "\x00");
$this->tables[$tag]['data'] = $data;
$this->tables[$tag]['length'] = $length;
$this->tables[$tag]['checkSum'] = $this->CheckSum($data);
}
function Seek($tag)
{
if(!isset($this->tables[$tag]))
$this->Error('Table not found: '.$tag);
fseek($this->f, $this->tables[$tag]['offset'], SEEK_SET);
}
function Skip($n)
{
fseek($this->f, $n, SEEK_CUR);
}
function Read($n)
{
return $n>0 ? fread($this->f, $n) : '';
}
function ReadUShort()
{
$a = unpack('nn', fread($this->f,2));
return $a['n'];
}
function ReadShort()
{
$a = unpack('nn', fread($this->f,2));
$v = $a['n'];
if($v>=0x8000)
$v -= 65536;
return $v;
}
function ReadULong()
{
$a = unpack('NN', fread($this->f,4));
return $a['N'];
}
function CheckSum($s)
{
$n = strlen($s);
$high = 0;
$low = 0;
for($i=0;$i<$n;$i+=4)
{
$high += (ord($s[$i])<<8) + ord($s[$i+1]);
$low += (ord($s[$i+2])<<8) + ord($s[$i+3]);
}
return pack('nn', $high+($low>>16), $low);
}
function Error($msg)
{
throw new Exception($msg);
}
}
?>
404: Not Found

2371
includes/fpdf/tfpdf.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -499,6 +499,7 @@ require_once 'includes/header.php';
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-4">
<form action="checkout.php" method="POST">
<input type="hidden" name="lang" value="<?= $lang ?>">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= $t['modal_title'] ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>

View File

@ -5,9 +5,45 @@ require_once 'mail/WablasService.php';
$session_id = $_GET['session_id'] ?? null;
$donation_id = $_GET['donation_id'] ?? null; // For simulation
$lang = $_GET['lang'] ?? 'ar';
if (!in_array($lang, ['en', 'ar'])) $lang = 'ar';
$dir = ($lang === 'ar') ? 'rtl' : 'ltr';
// UI Text
$texts = [
'en' => [
'title' => 'Donation Successful',
'thank_you' => 'Thank You!',
'success_msg' => 'Your donation has been successfully processed. Your generosity helps us continue our mission.',
'download_cert' => 'Download Certificate',
'return_home' => 'Return to Home',
'trans_id' => 'Transaction ID',
'status' => 'Status',
'completed' => 'Completed',
'conf_sent' => 'Confirmation messages have been sent to the relevant parties.',
'failed_title' => 'Payment Verification Failed',
'failed_msg' => 'We couldn\'t verify your payment. If you believe this is an error, please contact support.',
'back_home' => 'Back to Home'
],
'ar' => [
'title' => 'تم التبرع بنجاح',
'thank_you' => 'شكراً لك!',
'success_msg' => 'تمت معالجة تبرعك بنجاح. سخاؤك يساعدنا في الاستمرار في مهمتنا.',
'download_cert' => 'تحميل الشهادة',
'return_home' => 'العودة للرئيسية',
'trans_id' => 'رقم المعاملة',
'status' => 'الحالة',
'completed' => 'مكتمل',
'conf_sent' => 'تم إرسال رسائل التأكيد إلى الأطراف المعنية.',
'failed_title' => 'فشل التحقق من الدفع',
'failed_msg' => 'لم نتمكن من التحقق من دفعتك. إذا كنت تعتقد أن هذا خطأ، يرجى الاتصال بالدعم.',
'back_home' => 'العودة للرئيسية'
]
];
$t = $texts[$lang];
if (!$session_id) {
header('Location: index.php');
header('Location: index.php?lang=' . $lang);
exit;
}
@ -51,6 +87,16 @@ if ($donation) {
// Update case raised amount
$pdo->prepare("UPDATE cases SET raised = raised + ? WHERE id = ?")
->execute([$donation['amount'], $donation['case_id']]);
// Check if goal reached and mark as completed if so
$stmt = $pdo->prepare("SELECT raised, goal FROM cases WHERE id = ?");
$stmt->execute([$donation['case_id']]);
$updatedCase = $stmt->fetch();
if ($updatedCase && $updatedCase['raised'] >= $updatedCase['goal']) {
$pdo->prepare("UPDATE cases SET status = 'completed' WHERE id = ?")
->execute([$donation['case_id']]);
}
// Refresh donation data to get name/phone/gift info
$stmt = $pdo->prepare("SELECT * FROM donations WHERE id = ?");
@ -60,7 +106,7 @@ if ($donation) {
// Send WhatsApp notification to donor
WablasService::sendThankYou($fullDonation);
// Send WhatsApp notification to recipient if it's a gift
// Send WhatsApp notification to recipient if it\'s a gift
if ($fullDonation['is_gift']) {
WablasService::sendGiftNotification($fullDonation);
}
@ -79,21 +125,28 @@ if ($donation) {
}
$org = $pdo->query("SELECT * FROM org_profile LIMIT 1")->fetch();
$orgName = $org['name_en'] ?? 'Organization';
$orgName = ($lang === 'en') ? ($org['name_en'] ?? 'Organization') : ($org['name_ar'] ?? 'المؤسسة');
?>
<!DOCTYPE html>
<html lang="en">
<html lang="<?= $lang ?>" dir="<?= $dir ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Successful - <?= htmlspecialchars($orgName) ?></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<title><?= $t['title'] ?> - <?= htmlspecialchars($orgName) ?></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<?php if ($lang === 'ar'): ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css">
<?php else: ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<?php endif; ?>
<style>
body { background-color: #f8fafc; font-family: 'Inter', sans-serif; }
body { background-color: #f8fafc; font-family: 'Inter', system-ui, -apple-system, sans-serif; }
<?php if ($lang === 'ar'): ?>
body { font-family: 'Amiri', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
<?php endif; ?>
.success-card { max-width: 600px; border-radius: 24px; border: none; overflow: hidden; }
.success-icon { background: #ecfdf5; color: #10b981; width: 100px; height: 100px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto; }
.btn-home { background: #059669; color: white; border: none; padding: 12px 40px; border-radius: 12px; font-weight: 600; transition: all 0.3s; }
.btn-home { background: #059669; color: white; border: none; padding: 12px 40px; border-radius: 12px; font-weight: 600; transition: all 0.3s; text-decoration: none; }
.btn-home:hover { background: #047857; color: white; transform: translateY(-2px); }
.btn-cert { background: #fff; color: #059669; border: 2px solid #059669; padding: 12px 40px; border-radius: 12px; font-weight: 600; transition: all 0.3s; text-decoration: none; display: inline-block; }
.btn-cert:hover { background: #ecfdf5; color: #047857; }
@ -110,27 +163,27 @@ $orgName = $org['name_en'] ?? 'Organization';
<path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.42-6.447a.733.733 0 0 1 0 0z"/>
</svg>
</div>
<h1 class="fw-bold mb-3">Thank You!</h1>
<p class="text-muted fs-5 mb-4">Your donation has been successfully processed. Your generosity helps us continue our mission.</p>
<h1 class="fw-bold mb-3"><?= $t['thank_you'] ?></h1>
<p class="text-muted fs-5 mb-4"><?= $t['success_msg'] ?></p>
<div class="d-flex flex-column flex-sm-row gap-3 justify-content-center mb-5">
<a href="certificate.php?id=<?= $final_donation_id ?>" target="_blank" class="btn-cert">
<i class="bi bi-patch-check me-2"></i>Download Certificate
<a href="certificate.php?id=<?= $final_donation_id ?>&lang=<?= $lang ?>" target="_blank" class="btn-cert">
<i class="bi bi-patch-check me-2"></i><?= $t['download_cert'] ?>
</a>
<a href="index.php" class="btn btn-home">Return to Home</a>
<a href="index.php?lang=<?= $lang ?>" class="btn btn-home"><?= $t['return_home'] ?></a>
</div>
<div class="bg-light p-4 rounded-4 mb-4 text-start">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Transaction ID</span>
<span class="text-muted"><?= $t['trans_id'] ?></span>
<span class="fw-medium text-break"><?= htmlspecialchars($session_id) ?></span>
</div>
<div class="d-flex justify-content-between">
<span class="text-muted">Status</span>
<span class="badge bg-success rounded-pill px-3">Completed</span>
<span class="text-muted"><?= $t['status'] ?></span>
<span class="badge bg-success rounded-pill px-3"><?= $t['completed'] ?></span>
</div>
</div>
<p class="small text-muted mb-0">Confirmation messages have been sent to the relevant parties.</p>
<p class="small text-muted mb-0"><?= $t['conf_sent'] ?></p>
</div>
<?php else: ?>
<div class="card border-0 shadow-lg p-5 rounded-4">
@ -139,9 +192,9 @@ $orgName = $org['name_en'] ?? 'Organization';
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>
</svg>
</div>
<h2 class="fw-bold">Payment Verification Failed</h2>
<p class="text-muted">We couldn't verify your payment. If you believe this is an error, please contact support.</p>
<a href="index.php" class="btn btn-secondary rounded-pill px-4 mt-3">Back to Home</a>
<h2 class="fw-bold"><?= $t['failed_title'] ?></h2>
<p class="text-muted"><?= $t['failed_msg'] ?></p>
<a href="index.php?lang=<?= $lang ?>" class="btn btn-secondary rounded-pill px-4 mt-3"><?= $t['back_home'] ?></a>
</div>
<?php endif; ?>
</div>

1
tmp/ar-php-master.zip Normal file
View File

@ -0,0 +1 @@
Not Found

BIN
tmp/test_currency.pdf Normal file

Binary file not shown.