organize doc files and remove unecessary files
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
// In app/Config/Filters.php add the alias:
|
||||
public array $aliases = [
|
||||
'csrf' => \CodeIgniter\Filters\CSRF::class,
|
||||
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
|
||||
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
|
||||
'authFilter' => \App\Filters\AuthFilter::class,
|
||||
'apiDocsAuth' => \App\Filters\ApiDocsAuthFilter::class,
|
||||
];
|
||||
@@ -0,0 +1,296 @@
|
||||
# Job Postings Feature — Implementation Plan
|
||||
|
||||
## 1. Overview
|
||||
|
||||
This feature has two sides:
|
||||
|
||||
- **Admin side** — create/manage job position templates, publish open positions, review applicant submissions.
|
||||
- **Client side** — browse open positions, view details, and submit an application (with resume upload) that triggers a confirmation email.
|
||||
|
||||
---
|
||||
|
||||
## 2. Data Model
|
||||
|
||||
### JobTemplate
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `template_id` | UUID | Primary key |
|
||||
| `title` | string | |
|
||||
| `description` | text | |
|
||||
| `department` | string | |
|
||||
| `location` | string | |
|
||||
| `employment_type` | string | e.g. full-time, part-time, contract |
|
||||
| `requirements` | text | |
|
||||
| `salary_range` | string | optional |
|
||||
| `version` | int | increments on each edit |
|
||||
| `is_active` | bool | soft-archive instead of delete |
|
||||
| `created_by` | user ref | |
|
||||
| `created_at` / `updated_at` | timestamp | |
|
||||
|
||||
### JobPosition
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `position_id` | UUID | **Unique ID per job, required** |
|
||||
| `template_id` | UUID (nullable) | set if created from a template |
|
||||
| `title`, `description`, `department`, `location`, `employment_type`, `requirements`, `salary_range` | — | copied from template at creation time (not a live reference) |
|
||||
| `status` | enum | draft / open / closed / filled |
|
||||
| `created_at` / `updated_at` | timestamp | |
|
||||
| `posted_by` | user ref | |
|
||||
|
||||
### Application
|
||||
| Field | Type | Notes |
|
||||
|---|---|---|
|
||||
| `application_id` | UUID | |
|
||||
| `position_id` | FK | which job they applied to |
|
||||
| `first_name`, `last_name` | string | required |
|
||||
| `email` | string | required, validated |
|
||||
| `phone` | string | required |
|
||||
| `resume_file_url` | string | pointer to stored file |
|
||||
| `submitted_at` | timestamp | |
|
||||
| `status` | enum | new / reviewed / contacted / rejected / hired |
|
||||
| `admin_notes` | text | for admin follow-up tracking |
|
||||
|
||||
---
|
||||
|
||||
## 3. Admin Side
|
||||
|
||||
### 3.1 Create Open Position
|
||||
- Form fields: title, description, department, location, employment type, requirements, salary range.
|
||||
- Option to start **blank** or **from a template**.
|
||||
- On save: generate unique `position_id`, default status `draft` → publish sets `open`.
|
||||
|
||||
### 3.2 Job Templates (CRUD)
|
||||
- List / create / edit / archive templates.
|
||||
- **Editing behavior**: support both
|
||||
- **Overwrite** current version, or
|
||||
- **Save as new version** (keeps history)
|
||||
- Version history viewable/revertible.
|
||||
- Archiving a template never affects positions already created from it (fields are copied, not linked live).
|
||||
|
||||
### 3.3 Create Position from Template
|
||||
- Admin selects a template → fields pre-fill a new position form → admin edits as needed → save generates a new `position_id`.
|
||||
|
||||
### 3.4 Review Applications
|
||||
- Dashboard listing all submissions, filterable by position, status, or date.
|
||||
- Detail view: applicant info, resume preview/download, position applied to.
|
||||
- Status + notes fields so admin can track follow-up (contacted, rejected, hired).
|
||||
- Nice-to-have: CSV export, direct email link to applicant.
|
||||
|
||||
---
|
||||
|
||||
## 4. Client Side
|
||||
|
||||
### 4.1 Open Positions Listing
|
||||
- Public page listing all positions where `status = open`.
|
||||
- Card/list view: title, department, location, short summary.
|
||||
|
||||
### 4.2 Position Detail Page
|
||||
- Full description, requirements, etc.
|
||||
- "Apply" call-to-action.
|
||||
|
||||
### 4.3 Application Form
|
||||
- Fields: First Name, Last Name, Email, Phone, Resume upload (PDF/DOC).
|
||||
- Client-side validation: required fields, email format, file type/size limits.
|
||||
- Submits to create an `Application` tied to `position_id`.
|
||||
|
||||
### 4.4 Confirmation Email
|
||||
- Sent automatically to the applicant's email on successful submission.
|
||||
- References the position title and sets expectation of review/follow-up.
|
||||
- Optional: parallel notification email to admin/HR inbox.
|
||||
|
||||
---
|
||||
|
||||
## 5. API Endpoints (suggested)
|
||||
|
||||
**Admin**
|
||||
```
|
||||
POST /admin/templates
|
||||
PUT /admin/templates/:id
|
||||
GET /admin/templates
|
||||
|
||||
POST /admin/positions (optional template_id)
|
||||
PUT /admin/positions/:id
|
||||
GET /admin/positions
|
||||
|
||||
GET /admin/applications (filter by position, status)
|
||||
PATCH /admin/applications/:id (update status/notes)
|
||||
```
|
||||
|
||||
**Client (public)**
|
||||
```
|
||||
GET /positions (open only)
|
||||
GET /positions/:id
|
||||
POST /positions/:id/apply (multipart form, includes resume)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Infrastructure Notes
|
||||
|
||||
- **File storage**: resumes go to object storage (S3 or equivalent); store the file URL/key on the Application record, not the binary in the database.
|
||||
- **Email**: use a transactional email provider (SendGrid, SES, Postmark) for confirmation + admin notification emails, driven by templates.
|
||||
- **Auth**: admin routes require authenticated/role-gated access; client routes remain public.
|
||||
|
||||
---
|
||||
|
||||
## 7. Database Migrations
|
||||
|
||||
Assumes PostgreSQL syntax (adjust types for MySQL/SQLite as needed). Each migration is additive and ordered so foreign keys resolve correctly.
|
||||
|
||||
### Migration 001 — create `job_templates`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TABLE job_templates (
|
||||
template_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_job_templates_active ON job_templates (is_active);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_templates;
|
||||
```
|
||||
|
||||
### Migration 002 — create `job_template_versions` (version history)
|
||||
```sql
|
||||
-- up
|
||||
CREATE TABLE job_template_versions (
|
||||
version_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_id UUID NOT NULL REFERENCES job_templates(template_id) ON DELETE CASCADE,
|
||||
version INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
saved_by UUID REFERENCES users(id),
|
||||
saved_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_template_versions_template_id ON job_template_versions (template_id);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_template_versions;
|
||||
```
|
||||
|
||||
### Migration 003 — create `job_positions`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TYPE position_status AS ENUM ('draft', 'open', 'closed', 'filled');
|
||||
|
||||
CREATE TABLE job_positions (
|
||||
position_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
template_id UUID REFERENCES job_templates(template_id) ON DELETE SET NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
department VARCHAR(255),
|
||||
location VARCHAR(255),
|
||||
employment_type VARCHAR(50),
|
||||
requirements TEXT,
|
||||
salary_range VARCHAR(100),
|
||||
status position_status NOT NULL DEFAULT 'draft',
|
||||
posted_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_job_positions_status ON job_positions (status);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS job_positions;
|
||||
DROP TYPE IF EXISTS position_status;
|
||||
```
|
||||
|
||||
### Migration 004 — create `applications`
|
||||
```sql
|
||||
-- up
|
||||
CREATE TYPE application_status AS ENUM ('new', 'reviewed', 'contacted', 'rejected', 'hired');
|
||||
|
||||
CREATE TABLE applications (
|
||||
application_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
position_id UUID NOT NULL REFERENCES job_positions(position_id) ON DELETE CASCADE,
|
||||
first_name VARCHAR(100) NOT NULL,
|
||||
last_name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(30) NOT NULL,
|
||||
resume_file_url TEXT NOT NULL,
|
||||
status application_status NOT NULL DEFAULT 'new',
|
||||
admin_notes TEXT,
|
||||
submitted_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_applications_position_id ON applications (position_id);
|
||||
CREATE INDEX idx_applications_status ON applications (status);
|
||||
CREATE INDEX idx_applications_email ON applications (email);
|
||||
|
||||
-- down
|
||||
DROP TABLE IF EXISTS applications;
|
||||
DROP TYPE IF EXISTS application_status;
|
||||
```
|
||||
|
||||
### Migration 005 — updated_at auto-touch triggers (optional, Postgres)
|
||||
```sql
|
||||
-- up
|
||||
CREATE OR REPLACE FUNCTION set_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_job_templates_updated_at
|
||||
BEFORE UPDATE ON job_templates
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_job_positions_updated_at
|
||||
BEFORE UPDATE ON job_positions
|
||||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||||
|
||||
-- down
|
||||
DROP TRIGGER IF EXISTS trg_job_templates_updated_at ON job_templates;
|
||||
DROP TRIGGER IF EXISTS trg_job_positions_updated_at ON job_positions;
|
||||
DROP FUNCTION IF EXISTS set_updated_at();
|
||||
```
|
||||
|
||||
### Migration order & notes
|
||||
1. `job_templates` → 2. `job_template_versions` → 3. `job_positions` → 4. `applications` → 5. triggers.
|
||||
2. `users` table is assumed to already exist (for `created_by` / `posted_by`); drop those FK constraints if no auth/user table exists yet.
|
||||
3. Run each migration's `up` in order; `down` scripts reverse in the opposite order for rollback.
|
||||
4. If using a migration tool (Knex, Prisma, Sequelize, Alembic, Rails ActiveRecord, etc.), split each numbered migration above into that tool's file format/naming convention — the SQL logic stays the same.
|
||||
|
||||
---
|
||||
|
||||
## 8. Build Phases
|
||||
|
||||
| Phase | Scope |
|
||||
|---|---|
|
||||
| 1 | Data models + admin CRUD for Positions (no templates yet) |
|
||||
| 2 | Template CRUD + versioning + "create position from template" |
|
||||
| 3 | Client-facing listing + detail pages |
|
||||
| 4 | Application form + file upload + submission handling |
|
||||
| 5 | Confirmation email + admin notification email |
|
||||
| 6 | Admin review dashboard (applications list, status, notes) |
|
||||
| 7 | Polish: validation, admin auth/permissions, testing |
|
||||
|
||||
---
|
||||
|
||||
## 9. Open Questions
|
||||
|
||||
- How is admin access authenticated/restricted (login system, roles)?
|
||||
- Should duplicate applications (same email + position) be blocked or allowed?
|
||||
- What resume file types/size limits are acceptable?
|
||||
- Should closed positions remain visible (marked "closed") or disappear from the client list entirely?
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
// Add this group to app/Config/Routes.php
|
||||
$routes->group('api/v1', ['namespace' => 'App\Controllers\Api'], static function($routes) {
|
||||
// Assignments
|
||||
$routes->get('assignments', 'AssignmentController::index');
|
||||
$routes->post('assignments', 'AssignmentController::store');
|
||||
$routes->get('assignments/(:num)', 'AssignmentController::show/$1');
|
||||
$routes->put('assignments/(:num)', 'AssignmentController::update/$1');
|
||||
$routes->delete('assignments/(:num)', 'AssignmentController::delete/$1');
|
||||
$routes->get('assignments/student/(:num)', 'AssignmentController::student/$1');
|
||||
|
||||
// Attendance
|
||||
$routes->get('attendance', 'AttendanceController::index');
|
||||
$routes->post('attendance', 'AttendanceController::store');
|
||||
$routes->get('attendance/(:num)', 'AttendanceController::show/$1');
|
||||
$routes->put('attendance/(:num)', 'AttendanceController::update/$1');
|
||||
$routes->delete('attendance/(:num)', 'AttendanceController::delete/$1');
|
||||
$routes->get('attendance/student/(:num)', 'AttendanceController::student/$1');
|
||||
|
||||
// Attendance Tracking
|
||||
$routes->post('attendance-tracking/record', 'AttendanceTrackingController::record');
|
||||
$routes->get('attendance-tracking/violations', 'AttendanceTrackingController::violations');
|
||||
$routes->get('attendance-tracking/student/(:num)', 'AttendanceTrackingController::student/$1');
|
||||
|
||||
// Authorized Users
|
||||
$routes->get('authorized-users', 'AuthorizedUsersController::index');
|
||||
$routes->post('authorized-users', 'AuthorizedUsersController::store');
|
||||
$routes->get('authorized-users/(:num)', 'AuthorizedUsersController::show/$1');
|
||||
$routes->delete('authorized-users/(:num)', 'AuthorizedUsersController::delete/$1');
|
||||
|
||||
// Broadcast Email
|
||||
$routes->post('broadcast-email/send', 'BroadcastEmailController::send');
|
||||
$routes->get('broadcast-email/parents', 'BroadcastEmailController::parents');
|
||||
|
||||
// Calendar
|
||||
$routes->get('calendar', 'CalendarController::index');
|
||||
$routes->post('calendar', 'CalendarController::store');
|
||||
$routes->get('calendar/(:num)', 'CalendarController::show/$1');
|
||||
$routes->put('calendar/(:num)', 'CalendarController::update/$1');
|
||||
$routes->delete('calendar/(:num)', 'CalendarController::delete/$1');
|
||||
|
||||
// Classes
|
||||
$routes->get('classes', 'ClassController::index');
|
||||
$routes->get('classes/(:num)', 'ClassController::show/$1');
|
||||
$routes->get('classes/(:num)/students', 'ClassController::students/$1');
|
||||
|
||||
// Class Preparation
|
||||
$routes->get('class-preparation', 'ClassPreparationController::index');
|
||||
$routes->get('class-preparation/(:num)', 'ClassPreparationController::show/$1');
|
||||
$routes->post('class-preparation/calculate', 'ClassPreparationController::calculate');
|
||||
|
||||
// Communications
|
||||
$routes->get('communications', 'CommunicationController::index');
|
||||
$routes->post('communications/send', 'CommunicationController::send');
|
||||
$routes->get('communications/conversation', 'CommunicationController::conversation');
|
||||
$routes->delete('communications/(:num)', 'CommunicationController::delete/$1');
|
||||
});
|
||||
|
||||
// API Docs routes
|
||||
$routes->get('docs', 'App\Controllers\DocsController::index');
|
||||
$routes->get('docs/api', 'App\Controllers\ApiDocsController::index', ['filter' => 'apiDocsAuth']);
|
||||
$routes->get('docs/api/public', 'App\Controllers\ApiDocsController::public');
|
||||
@@ -0,0 +1,94 @@
|
||||
<!--
|
||||
Steps for enrollment process:
|
||||
Admission under review
|
||||
Payment pending //we should generate invoice
|
||||
Enrolled
|
||||
|
||||
|
||||
Steps for Withdrawn:
|
||||
Withdraw under review
|
||||
Refund pending
|
||||
After providing refund if any -> Withdrawn
|
||||
|
||||
|
||||
add Withdraw deadline at configuration table
|
||||
|
||||
|
||||
##########################################################################################################################
|
||||
cron job command for paypal to update payment table:
|
||||
*/15 * * * * /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark sync_paypal_payments >> /home/u280815660/domains/alrahmaisgl.org/alrahma/writable/logs/paypal_cron.log 2>&1
|
||||
|
||||
###########################################################################################################################
|
||||
|
||||
|
||||
dX7!aPz9#LmqR2@t
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
curl -i -X POST https://test.alrahmaisgl.org/api/paypal-webhook \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @payload.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
curl -i -X POST http://localhost:8080/api/paypal-webhook \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @payload.json
|
||||
|
||||
|
||||
|
||||
get:
|
||||
'balance', and 'paid_amount', from invoice table
|
||||
invoice table has parent_id = user_id
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
webhook_id, client_id, and secret should ideally be stored in .env or config files.
|
||||
|
||||
|
||||
|
||||
################################################
|
||||
<table id="myTable" class="display">
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#myTable').DataTable();
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
#####################################################
|
||||
|
||||
|
||||
when the payment status=paid update the enrollment status = enrolled
|
||||
refund amount at http://localhost:8080/invoice_payment/invoice_management need to be updated
|
||||
need to track the refund status either refunded or not
|
||||
|
||||
################################################################################
|
||||
#very important to add to deployment script
|
||||
If the file is currently in writable/uploads/checks/, then you need to:
|
||||
# From project root
|
||||
ln -s writable/uploads public/uploads
|
||||
|
||||
Then it can be accessed via:
|
||||
<?= base_url('uploads/checks/' . $refund['check_file']) ?>
|
||||
##########################################################################
|
||||
|
||||
|
||||
I want to design pages to show everything about the school:
|
||||
financial report
|
||||
number of students
|
||||
number of teachers
|
||||
number of admins
|
||||
number of parents
|
||||
expenses
|
||||
number of tables chairs
|
||||
@@ -0,0 +1,52 @@
|
||||
# Scope Discipline Rules
|
||||
|
||||
These rules override any general instinct to "improve while I'm in there." Follow them on every task, no exceptions.
|
||||
|
||||
## Before touching any code
|
||||
|
||||
1. Restate the task in one sentence: what behavior must change, and what the expected outcome is.
|
||||
2. Identify the smallest set of files/functions responsible for that behavior. This is your **allowed scope**. Everything else is **protected**.
|
||||
3. Read the relevant code before editing it. Do not edit based on assumptions about how it probably works.
|
||||
|
||||
## The hard rule: ask before expanding scope
|
||||
|
||||
If, while working, you find that:
|
||||
- a file outside your allowed scope needs to change,
|
||||
- a dependency needs to be added/updated,
|
||||
- a test needs modification,
|
||||
- an unrelated bug is blocking you,
|
||||
- or a "cleaner" implementation would touch more than the minimum,
|
||||
|
||||
**stop and ask me before making that change.** Explain:
|
||||
- what you were trying to do,
|
||||
- why the fix requires going outside the original scope,
|
||||
- exactly what you want to change and where.
|
||||
|
||||
Wait for my answer. Do not proceed on your own judgment, even if you're confident it's correct or trivial.
|
||||
|
||||
This applies even to small things (renaming a variable for clarity, fixing a typo in an unrelated comment, reformatting a block you had to scroll past). If it's not required to satisfy the request, ask first.
|
||||
|
||||
## While editing
|
||||
|
||||
- Make the fewest-line, fewest-file change that correctly satisfies the request.
|
||||
- Preserve existing naming, structure, patterns, and formatting. Match the codebase's existing style, don't impose your own.
|
||||
- Never run project-wide formatters/linters-with-autofix/import-organizers as a side effect of a small change.
|
||||
- Never touch tests except to add new ones that validate the requested behavior — and only after confirming that's in scope.
|
||||
- Treat any uncommitted/staged changes already in the working tree as off-limits. Don't revert, reset, or absorb them into your edit.
|
||||
|
||||
## Before reporting done
|
||||
|
||||
Review your own diff, file by file, line by line. For anything you can't justify with "this was required by the explicit request," revert it.
|
||||
|
||||
Then report:
|
||||
- **Files changed** — list, with a one-line reason each tied directly to the request.
|
||||
- **Scope confirmation** — explicitly state: "No files, dependencies, tests, or config outside this list were modified."
|
||||
- **Anything you noticed but didn't touch** — unrelated bugs, tech debt, cleanup opportunities. Mention them, don't fix them.
|
||||
|
||||
## If the task genuinely can't be done without expanding scope
|
||||
|
||||
Say so plainly, explain what would need to change and why, and wait for confirmation. Don't silently do the bigger version, and don't pretend a partial/incorrect fix is complete.
|
||||
|
||||
---
|
||||
|
||||
**Default when uncertain: don't make the change, ask instead.**
|
||||
@@ -0,0 +1,34 @@
|
||||
- Islamic Studies - Student Workbook - Level 8: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 7: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 6: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 5: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 4: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 3: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 2: **$5.00**
|
||||
- Islamic Studies - Student Workbook - Level 1: **$5.00**
|
||||
- Arabic Writing Workbook: **$11.00**
|
||||
- Beginners Arabic Reading: **$6.00**
|
||||
- Ready to Write Alif Ba Ta: **$11.00**
|
||||
- Teacher's Manual - Level 8: **$20.00**
|
||||
- Teacher's Manual - Level 7: **$20.00**
|
||||
- Teacher's Manual - Level 6: **$20.00**
|
||||
- Teacher's Manual - Level 5: **$20.00**
|
||||
- Teacher's Manual - Level 4: **$20.00**
|
||||
- Teacher's Manual - Level 3: **$20.00**
|
||||
- Teacher's Manual - Level 2: **$20.00**
|
||||
- Teacher's Manual - Level 1: **$20.00**
|
||||
- Juz Tabarak: **$14.00**
|
||||
- Juz Amma Workbook - Vol 2: **$8.00**
|
||||
- Juz Amma Workbook - Vol 1: **$8.00**
|
||||
- Juz Amma Workbook - Vol 1 (B&W version): **$4.00**
|
||||
- Juz Amma for School Students: **$14.00**
|
||||
- Islamic Studies Level 9 (Revised and Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 8 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 7 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 6 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 5 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 4 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 3 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 2 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level 1 (Revised & Enlarged Edition): **$17.00**
|
||||
- Islamic Studies Level K (Revised & Enlarged Edition): **$17.00**
|
||||
@@ -0,0 +1,64 @@
|
||||
# Careers Section (Home Page) — Implementation Plan
|
||||
|
||||
## 1. Goals & Scope
|
||||
- Attract candidates and showcase company culture directly from the home page
|
||||
- Careers lives as a **section on the home page**, not a standalone `/careers` page
|
||||
- Section should link out to a full job detail view or an ATS for the actual application step
|
||||
- Decide: in-house application handling vs. linking out to an ATS (Greenhouse, Lever, Workable, etc.)
|
||||
|
||||
## 2. Content Structure (Home Page Section)
|
||||
- **Section heading/hero** — tagline + short pitch on why to work here
|
||||
- **Culture/values snippet** — a few photos or highlights, benefits teaser (health, remote work, PTO, equity, etc.)
|
||||
- **Open positions preview** — short list (e.g., top 3–5) with title, department, location, type
|
||||
- **"View all openings" CTA** — links to full listing (could be a modal, an anchor-expanded list, or an external ATS board)
|
||||
- **Application CTA** — "Apply Now" per role, linking to a form, email, or ATS
|
||||
- **Optional extras** — employee testimonials, office photos/video, perks grid
|
||||
|
||||
## 3. Data Model
|
||||
Fields per job posting:
|
||||
```
|
||||
title
|
||||
department
|
||||
location
|
||||
employment_type
|
||||
description
|
||||
requirements
|
||||
salary_range (optional)
|
||||
posted_date
|
||||
status (open/closed)
|
||||
apply_link
|
||||
```
|
||||
Store in a CMS (Sanity, Contentful, Strapi) or a simple JSON/database table if no CMS exists.
|
||||
|
||||
## 4. Technical Approach
|
||||
- **Static list** — hardcode JSON if roles change rarely
|
||||
- **CMS-driven** — non-technical team can add/remove postings without code
|
||||
- **ATS integration** — pull live listings via API (Greenhouse/Lever both offer public job board APIs) for least maintenance
|
||||
- **Application handling** — form → email service (e.g., SendGrid) or direct link to ATS application page
|
||||
|
||||
## 5. Pages / Routes
|
||||
- `/` (home page) — Careers section embedded, e.g. `#careers` anchor for nav linking
|
||||
- `/careers/[job-slug]` — individual job detail page (linked from the home section)
|
||||
- `/careers/apply/[job-slug]` — application form (optional)
|
||||
- Consider adding "Careers" to the main nav, scrolling/linking to the `#careers` section
|
||||
|
||||
## 6. Design Considerations
|
||||
- Keep the section concise — it's part of the home page, not the main focus, so limit to a handful of featured/open roles
|
||||
- Mobile-responsive job cards, and ensure the section doesn't overload home page load time
|
||||
- Clear, prominent "View openings" / "Apply" CTAs
|
||||
- SEO: `JobPosting` schema.org structured data on the linked job detail pages so roles surface in Google Jobs search
|
||||
- Make sure the section is reachable via nav (e.g., "Careers" nav item scrolls to the section or links to `/#careers`)
|
||||
|
||||
## 7. Build Order
|
||||
1. Design mockups/wireframe for the home page section (placement, hero, mini job list)
|
||||
2. Set up data source (CMS or ATS API) for job postings
|
||||
3. Build the home page Careers section + "view all/apply" links
|
||||
4. Build job detail page(s) linked from the section
|
||||
5. Wire up application flow
|
||||
6. Add SEO schema markup on job detail pages
|
||||
7. Test on mobile; submit a test application
|
||||
8. Launch and monitor applications
|
||||
|
||||
## Open Questions
|
||||
- What is the site built with (React/Next.js, WordPress, Webflow, plain HTML)?
|
||||
- Use an existing ATS (Greenhouse/Lever) or handle applications via email/form?
|
||||
@@ -0,0 +1,28 @@
|
||||
# Set env + cd to project so Spark loads correctly
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
CI_ENVIRONMENT=production
|
||||
|
||||
|
||||
# Sundays @ 9:50 AM — turn ON
|
||||
50 9 * * 0 cd /opt/lampp/htdocs/alrahma_school_sunday && /usr/bin/php spark config:update -t enable_attendance_on --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
|
||||
# Sundays @ 1:00 PM — turn OFF
|
||||
0 13 * * 0 cd /opt/lampp/htdocs/alrahma_school_sunday && /usr/bin/php spark config:update -t enable_attendance_off --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
|
||||
# June 1 @ 00:10 America/New_York
|
||||
10 0 1 6 * cd /opt/lampp/htdocs/alrahma_school_sunday && /usr/bin/php spark config:update -t update_date_age_reference --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
|
||||
# Daily @ 8:00 AM - send registration opening email only when today matches school_years.registration_starts_on
|
||||
0 8 * * * cd /opt/lampp/htdocs/alrahma_school_sunday && /usr/bin/php spark registration:send-opening-email --tz=America/New_York >> /var/log/ci4_registration_opening_email.log 2>&1
|
||||
|
||||
*/15 * * * * /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark users:delete-inactive-users
|
||||
*/15 * * * * /usr/bin/php /home/u280815660/domains/test.alrahmaisgl.org/alrahma/spark users:delete-inactive-users
|
||||
0 2 * * * /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark payments:sync-paypal >> /home/u280815660/domains/alrahmaisgl.org/alrahma/writable/logs/paypal_cron.log 2>&1
|
||||
50 9 * * 7 /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark config:update -t enable_attendance_on --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
0 13 * * 7 /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark config:update -t enable_attendance_off --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
0 0 1 7 * /usr/bin/php /home/u280815660/domains/alrahmaisgl.org/alrahma/spark config:update -t update_date_age_reference --tz=America/New_York >> /var/log/ci4_config_update.log 2>&1
|
||||
0 2 * * * php /path/to/your/project/public/index.php notifications:cleanup >> /path/to/your/project/writable/logs/cron_cleanup.log 2>&1
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#--------------------------------------------------------------------
|
||||
# Example Environment Configuration file
|
||||
#
|
||||
# This file can be used as a starting point for your own
|
||||
# custom .env files, and contains most of the possible settings
|
||||
# available in a default install.
|
||||
#
|
||||
# By default, all of the settings are commented out. If you want
|
||||
# to override the setting, you must un-comment it by removing the '#'
|
||||
# at the beginning of the line.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
CI_ENVIRONMENT = development
|
||||
|
||||
# Which profile to use if none is supplied to sendEmail()
|
||||
# Options: default, communication, payment, ...
|
||||
MAIL_PROFILE_DEFAULT=default
|
||||
|
||||
# ===== DEFAULT (system) =====
|
||||
# ---- Legacy fallbacks (kept for compatibility) ----
|
||||
SMTP_HOST = smtp.gmail.com
|
||||
SMTP_USER = alrahma.sunday.school@gmail.com
|
||||
SMTP_PASS = "psnp emdq dykw ypul"
|
||||
SMTP_PORT = 587
|
||||
SMTP_ENCRYPTION = tls # was SSL; set to ssl to match 587
|
||||
|
||||
# Reply-To for all mail profiles
|
||||
MAIL_DEFAULT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_DEFAULT_REPLY_TO_NAME="Al Rahma Sunday School"
|
||||
MAIL_COMMUNICATION_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_COMMUNICATION_REPLY_TO_NAME="School Communications"
|
||||
MAIL_PAYMENT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_PAYMENT_REPLY_TO_NAME="School Payments"
|
||||
MAIL_DEFAULT_REPLY_TO="alrahma.isgl@gmail.com"
|
||||
|
||||
# Optional sender directory you already use elsewhere
|
||||
MAIL_SENDERS="{\"general\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma No-Reply\"},\
|
||||
\"registration\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Register Office\"},\
|
||||
\"notifications\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Notifications\"},\
|
||||
\"finance\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Finance Office\"}}"
|
||||
|
||||
# Principal notification recipient for TimeOff requests (comma-separated allowed)
|
||||
PRINCIPAL_EMAIL="principal@alrahmaisgl.org"
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
app_baseURL = 'http://localhost:8080/'
|
||||
# app.forceGlobalSecureRequests = true
|
||||
# app.CSPEnabled = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
database.default.hostname = 127.0.0.1
|
||||
database.default.database = school
|
||||
database.default.username = root
|
||||
database.default.password =
|
||||
database.default.DBDriver = MySQLi
|
||||
database.default.DBPrefix =
|
||||
database.default.port = 3306
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CONTENT SECURITY POLICY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# contentsecuritypolicy.reportOnly = false
|
||||
# contentsecuritypolicy.defaultSrc = 'none'
|
||||
# contentsecuritypolicy.scriptSrc = 'self'
|
||||
# contentsecuritypolicy.styleSrc = 'self'
|
||||
# contentsecuritypolicy.imageSrc = 'self'
|
||||
# contentsecuritypolicy.baseURI = null
|
||||
# contentsecuritypolicy.childSrc = null
|
||||
# contentsecuritypolicy.connectSrc = 'self'
|
||||
# contentsecuritypolicy.fontSrc = null
|
||||
# contentsecuritypolicy.formAction = null
|
||||
# contentsecuritypolicy.frameAncestors = null
|
||||
# contentsecuritypolicy.frameSrc = null
|
||||
# contentsecuritypolicy.mediaSrc = null
|
||||
# contentsecuritypolicy.objectSrc = null
|
||||
# contentsecuritypolicy.pluginTypes = null
|
||||
# contentsecuritypolicy.reportURI = null
|
||||
# contentsecuritypolicy.sandbox = false
|
||||
# contentsecuritypolicy.upgradeInsecureRequests = false
|
||||
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
|
||||
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
|
||||
# contentsecuritypolicy.autoNonce = true
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# COOKIE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# cookie.prefix = ''
|
||||
# cookie.expires = 0
|
||||
# cookie.path = '/'
|
||||
# cookie.domain = ''
|
||||
# cookie.secure = false
|
||||
# cookie.httponly = false
|
||||
# cookie.samesite = 'Lax'
|
||||
# cookie.raw = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENCRYPTION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# encryption.key =
|
||||
# encryption.driver = OpenSSL
|
||||
# encryption.blockSize = 16
|
||||
# encryption.digest = SHA512
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# HONEYPOT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# honeypot.hidden = 'true'
|
||||
# honeypot.label = 'Fill This Field'
|
||||
# honeypot.name = 'honeypot'
|
||||
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
|
||||
# honeypot.container = '<div style="display:none">{template}</div>'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SECURITY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# security.csrfProtection = 'cookie'
|
||||
# security.tokenRandomize = false
|
||||
# security.tokenName = 'csrf_token_name'
|
||||
# security.headerName = 'X-CSRF-TOKEN'
|
||||
# security.cookieName = 'csrf_cookie_name'
|
||||
# security.expires = 7200
|
||||
# security.regenerate = true
|
||||
# security.redirect = false
|
||||
# security.samesite = 'Lax'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SESSION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# session.cookieName = 'ci_session'
|
||||
# session.expiration = 7200
|
||||
# session.savePath = null
|
||||
# session.matchIP = false
|
||||
# session.timeToUpdate = 300
|
||||
# session.regenerateDestroy = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# LOGGER
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# logger.threshold = 4
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CURLRequest
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# curlrequest.shareOptions = false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mail.protocol = smtp
|
||||
mail.SMTPHost = ${SMTP_HOST}
|
||||
mail.SMTPUser = ${SMTP_USER}
|
||||
mail.SMTPPass = ${SMTP_PASS}
|
||||
mail.SMTPPort = ${SMTP_PORT}
|
||||
mail.SMTPCrypto = tls
|
||||
|
||||
mail.fromEmail = ${SMTP_USER}
|
||||
mail.fromName = "Al Rahma Sunday School"
|
||||
mail.mailType = text
|
||||
mail.charset = UTF-8
|
||||
@@ -0,0 +1,94 @@
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
CI_ENVIRONMENT = development
|
||||
|
||||
# Which profile to use if none is supplied to sendEmail()
|
||||
# Options: default, communication, payment, ...
|
||||
MAIL_PROFILE_DEFAULT=default
|
||||
|
||||
# ===== DEFAULT (system) =====
|
||||
MAIL_DEFAULT_HOST=smtp.hostinger.com
|
||||
MAIL_DEFAULT_USER=no-replay@alrahmaisgl.org
|
||||
MAIL_DEFAULT_PASS="W8;xZ/5g"
|
||||
MAIL_DEFAULT_PORT=587
|
||||
MAIL_DEFAULT_ENCRYPTION=ssl
|
||||
MAIL_DEFAULT_FROM_EMAIL=no-replay@alrahmaisgl.org
|
||||
MAIL_DEFAULT_FROM_NAME="Al Rahma Sunday School"
|
||||
# Optional Reply-To (uncomment if you want a replyable inbox)
|
||||
MAIL_DEFAULT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_DEFAULT_REPLY_TO_NAME="No-Reply"
|
||||
# Optional bounce address (envelope sender)
|
||||
# MAIL_DEFAULT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
# Optional DKIM
|
||||
# MAIL_DEFAULT_DKIM_DOMAIN=alrahmaisgl.org
|
||||
# MAIL_DEFAULT_DKIM_SELECTOR=default
|
||||
# MAIL_DEFAULT_DKIM_PRIVATE=/path/to/private.key
|
||||
|
||||
# ===== COMMUNICATION =====
|
||||
MAIL_COMMUNICATION_HOST=smtp.hostinger.com
|
||||
MAIL_COMMUNICATION_USER=no-replay@alrahmaisgl.org
|
||||
MAIL_COMMUNICATION_PASS="W8;xZ/5g"
|
||||
MAIL_COMMUNICATION_PORT=587
|
||||
MAIL_COMMUNICATION_ENCRYPTION=ssl
|
||||
MAIL_COMMUNICATION_FROM_EMAIL=no-replay@alrahmaisgl.org
|
||||
MAIL_COMMUNICATION_FROM_NAME="School Communications"
|
||||
MAIL_COMMUNICATION_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_COMMUNICATION_REPLY_TO_NAME="School Communications"
|
||||
# MAIL_COMMUNICATION_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ===== PAYMENT =====
|
||||
MAIL_PAYMENT_HOST=smtp.hostinger.com
|
||||
MAIL_PAYMENT_USER=no-replay@alrahmaisgl.org
|
||||
MAIL_PAYMENT_PASS="W8;xZ/5g"
|
||||
MAIL_PAYMENT_PORT=587
|
||||
MAIL_PAYMENT_ENCRYPTION=ssl
|
||||
MAIL_PAYMENT_FROM_EMAIL=no-replay@alrahmaisgl.org
|
||||
MAIL_PAYMENT_FROM_NAME="School Payments"
|
||||
MAIL_PAYMENT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_PAYMENT_REPLY_TO_NAME="School Payments"
|
||||
# MAIL_PAYMENT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ---- Legacy fallbacks (kept for compatibility) ----
|
||||
SMTP_HOST = smtp.hostinger.com
|
||||
SMTP_USER = no-replay@alrahmaosgl.org
|
||||
SMTP_PASS = "W8;xZ/5g"
|
||||
SMTP_PORT = 587
|
||||
SMTP_ENCRYPTION = ssl # was SSL; set to ssl to match 587
|
||||
|
||||
# Optional sender directory you already use elsewhere
|
||||
MAIL_SENDERS="{\"general\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma No-Reply\"},\
|
||||
\"registration\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Register Office\"},\
|
||||
\"notifications\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Notifications\"},\
|
||||
\"finance\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Finance Office\"}}"
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
app.baseURL = 'http://localhost:8080/'
|
||||
app.forceGlobalSecureRequests = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
database.default.hostname = 127.0.0.1
|
||||
database.default.database = school
|
||||
database.default.username = root
|
||||
database.default.password =
|
||||
database.default.DBDriver = MySQLi
|
||||
database.default.DBPrefix =
|
||||
database.default.port = 3306
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CODEIGNITER EMAIL (only used if you use CI Email class)
|
||||
# These don't affect PHPMailer but keep them aligned anyway.
|
||||
mail.protocol = smtp
|
||||
mail.SMTPHost = ${SMTP_HOST}
|
||||
mail.SMTPUser = ${SMTP_USER}
|
||||
mail.SMTPPass = ${SMTP_PASS}
|
||||
mail.SMTPPort = ${SMTP_PORT}
|
||||
mail.SMTPCrypto = ssl
|
||||
mail.fromEmail = ${SMTP_USER}
|
||||
mail.fromName = "Al Rahma Sunday School"
|
||||
mail.mailType = html
|
||||
mail.charset = UTF-8
|
||||
@@ -0,0 +1,242 @@
|
||||
#--------------------------------------------------------------------
|
||||
# Example Environment Configuration file
|
||||
#
|
||||
# This file can be used as a starting point for your own
|
||||
# custom .env files, and contains most of the possible settings
|
||||
# available in a default install.
|
||||
#
|
||||
# By default, all of the settings are commented out. If you want
|
||||
# to override the setting, you must un-comment it by removing the '#'
|
||||
# at the beginning of the line.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
CI_ENVIRONMENT = production
|
||||
|
||||
# Which profile to use if none is supplied to sendEmail()
|
||||
# Options: default, communication, payment, ...
|
||||
MAIL_PROFILE_DEFAULT=default
|
||||
|
||||
# ===== DEFAULT (system) =====
|
||||
MAIL_DEFAULT_HOST=smtp.gmail.com
|
||||
MAIL_DEFAULT_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_DEFAULT_PASS="psnp emdq dykw ypul"
|
||||
MAIL_DEFAULT_PORT=587
|
||||
MAIL_DEFAULT_ENCRYPTION=tls
|
||||
MAIL_DEFAULT_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_DEFAULT_FROM_NAME="Al Rahma Sunday School"
|
||||
# Optional Reply-To (uncomment if you want a replyable inbox)
|
||||
# MAIL_DEFAULT_REPLY_TO=communications@alrahmaisgl.org
|
||||
MAIL_DEFAULT_REPLY_TO_NAME="No-Reply"
|
||||
# Optional bounce address (envelope sender)
|
||||
# MAIL_DEFAULT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
# Optional DKIM
|
||||
# MAIL_DEFAULT_DKIM_DOMAIN=alrahmaisgl.org
|
||||
# MAIL_DEFAULT_DKIM_SELECTOR=default
|
||||
# MAIL_DEFAULT_DKIM_PRIVATE=/path/to/private.key
|
||||
|
||||
# ===== COMMUNICATION =====
|
||||
MAIL_COMMUNICATION_HOST=smtp.gmail.com
|
||||
MAIL_COMMUNICATION_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_COMMUNICATION_PASS="psnp emdq dykw ypul"
|
||||
MAIL_COMMUNICATION_PORT=587
|
||||
MAIL_COMMUNICATION_ENCRYPTION=tls
|
||||
MAIL_COMMUNICATION_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_COMMUNICATION_FROM_NAME="School Communications"
|
||||
MAIL_COMMUNICATION_REPLY_TO=alrahma.sunday.school@gmail.com
|
||||
MAIL_COMMUNICATION_REPLY_TO_NAME="School Communications"
|
||||
# MAIL_COMMUNICATION_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ===== PAYMENT =====
|
||||
MAIL_PAYMENT_HOST=smtp.gmail.com
|
||||
MAIL_PAYMENT_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_PAYMENT_PASS="psnp emdq dykw ypul"
|
||||
MAIL_PAYMENT_PORT=587
|
||||
MAIL_PAYMENT_ENCRYPTION=tls
|
||||
MAIL_PAYMENT_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_PAYMENT_FROM_NAME="School Payments"
|
||||
|
||||
# MAIL_PAYMENT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ---- Legacy fallbacks (kept for compatibility) ----
|
||||
SMTP_HOST = smtp.gmail.com
|
||||
SMTP_USER = alrahma.sunday.school@gmail.com
|
||||
SMTP_PASS = "psnp emdq dykw ypul"
|
||||
SMTP_PORT = 587
|
||||
SMTP_ENCRYPTION = tls # was SSL; set to ssl to match 587
|
||||
|
||||
# Reply-To for all mail profiles
|
||||
MAIL_DEFAULT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_DEFAULT_REPLY_TO_NAME="Al Rahma Sunday School"
|
||||
MAIL_COMMUNICATION_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_COMMUNICATION_REPLY_TO_NAME="School Communications"
|
||||
MAIL_PAYMENT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_PAYMENT_REPLY_TO_NAME="School Payments"
|
||||
|
||||
# Optional sender directory you already use elsewhere
|
||||
MAIL_SENDERS="{\"general\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma No-Reply\"},\
|
||||
\"registration\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Register Office\"},\
|
||||
\"notifications\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Notifications\"},\
|
||||
\"finance\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Finance Office\"}}"
|
||||
|
||||
# Principal notification recipient for TimeOff requests (comma-separated allowed)
|
||||
|
||||
PRINCIPAL_EMAIL="principal@alrahmaisgl.org"
|
||||
MAIL_PARENT_REPORT_TO="alrahma.isgl@gmail.com"
|
||||
MAIL_FROM_ADDRESS = "alrahma.sunday.school@gmail.com"
|
||||
session.expiration = 43200
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
app.baseURL = 'https://test.alrahmaisgl.org/'
|
||||
# app.forceGlobalSecureRequests = true
|
||||
# app.CSPEnabled = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
database.default.hostname = localhost
|
||||
database.default.database = u280815660_school
|
||||
database.default.username = u280815660_melabidi
|
||||
database.default.password = >tNxlRzP/W8
|
||||
database.default.DBDriver = MySQLi
|
||||
database.default.DBPrefix =
|
||||
database.default.port = 3306
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CONTENT SECURITY POLICY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# contentsecuritypolicy.reportOnly = false
|
||||
# contentsecuritypolicy.defaultSrc = 'none'
|
||||
# contentsecuritypolicy.scriptSrc = 'self'
|
||||
# contentsecuritypolicy.styleSrc = 'self'
|
||||
# contentsecuritypolicy.imageSrc = 'self'
|
||||
# contentsecuritypolicy.baseURI = null
|
||||
# contentsecuritypolicy.childSrc = null
|
||||
# contentsecuritypolicy.connectSrc = 'self'
|
||||
# contentsecuritypolicy.fontSrc = null
|
||||
# contentsecuritypolicy.formAction = null
|
||||
# contentsecuritypolicy.frameAncestors = null
|
||||
# contentsecuritypolicy.frameSrc = null
|
||||
# contentsecuritypolicy.mediaSrc = null
|
||||
# contentsecuritypolicy.objectSrc = null
|
||||
# contentsecuritypolicy.pluginTypes = null
|
||||
# contentsecuritypolicy.reportURI = null
|
||||
# contentsecuritypolicy.sandbox = false
|
||||
# contentsecuritypolicy.upgradeInsecureRequests = false
|
||||
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
|
||||
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
|
||||
# contentsecuritypolicy.autoNonce = true
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# COOKIE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# cookie.prefix = ''
|
||||
# cookie.expires = 0
|
||||
# cookie.path = '/'
|
||||
# cookie.domain = ''
|
||||
# cookie.secure = false
|
||||
# cookie.httponly = false
|
||||
# cookie.samesite = 'Lax'
|
||||
# cookie.raw = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENCRYPTION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# encryption.key =
|
||||
# encryption.driver = OpenSSL
|
||||
# encryption.blockSize = 16
|
||||
# encryption.digest = SHA512
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# HONEYPOT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# honeypot.hidden = 'true'
|
||||
# honeypot.label = 'Fill This Field'
|
||||
# honeypot.name = 'honeypot'
|
||||
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
|
||||
# honeypot.container = '<div style="display:none">{template}</div>'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SECURITY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# security.csrfProtection = 'cookie'
|
||||
# security.tokenRandomize = false
|
||||
# security.tokenName = 'csrf_token_name'
|
||||
# security.headerName = 'X-CSRF-TOKEN'
|
||||
# security.cookieName = 'csrf_cookie_name'
|
||||
# security.expires = 7200
|
||||
# security.regenerate = true
|
||||
# security.redirect = false
|
||||
# security.samesite = 'Lax'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SESSION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# session.cookieName = 'ci_session'
|
||||
# session.expiration = 7200
|
||||
# session.savePath = null
|
||||
# session.matchIP = false
|
||||
# session.timeToUpdate = 300
|
||||
# session.regenerateDestroy = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# LOGGER
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# logger.threshold = 4
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CURLRequest
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# curlrequest.shareOptions = false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mail.protocol = smtp
|
||||
mail.SMTPHost = ${SMTP_HOST}
|
||||
mail.SMTPUser = ${SMTP_USER}
|
||||
mail.SMTPPass = ${SMTP_PASS}
|
||||
mail.SMTPPort = ${SMTP_PORT}
|
||||
mail.SMTPCrypto = tls
|
||||
|
||||
mail.fromEmail = ${SMTP_USER}
|
||||
mail.fromName = "Al Rahma Sunday School"
|
||||
mail.mailType = text
|
||||
mail.charset = UTF-8
|
||||
|
||||
|
||||
|
||||
|
||||
# ---- Printer Mode (network, usb, windows, or disabled)
|
||||
PRINTER_MODE=disabled
|
||||
|
||||
# ---- Network printer settings
|
||||
PRINTER_HOST=192.168.1.100
|
||||
PRINTER_PORT=9100
|
||||
|
||||
# ---- USB printer settings (used when PRINTER_MODE=usb)
|
||||
PRINTER_USB_VID=0x0dd4
|
||||
PRINTER_USB_PID=0x0285
|
||||
PRINTER_WINDOWS_NAME="CUSTOM P3L"
|
||||
|
||||
# ---- Formatting
|
||||
# 80mm paper ~ 48-56 characters per line
|
||||
PRINTER_CHARS_PER_LINE=48
|
||||
PRINTER_FEED_LINES=3
|
||||
@@ -0,0 +1,211 @@
|
||||
#--------------------------------------------------------------------
|
||||
# Example Environment Configuration file
|
||||
#
|
||||
# This file can be used as a starting point for your own
|
||||
# custom .env files, and contains most of the possible settings
|
||||
# available in a default install.
|
||||
#
|
||||
# By default, all of the settings are commented out. If you want
|
||||
# to override the setting, you must un-comment it by removing the '#'
|
||||
# at the beginning of the line.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
CI_ENVIRONMENT = production
|
||||
|
||||
# Which profile to use if none is supplied to sendEmail()
|
||||
# Options: default, communication, payment, ...
|
||||
MAIL_PROFILE_DEFAULT=default
|
||||
|
||||
# ===== DEFAULT (system) =====
|
||||
MAIL_DEFAULT_HOST=smtp.gmail.com
|
||||
MAIL_DEFAULT_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_DEFAULT_PASS="psnp emdq dykw ypul"
|
||||
MAIL_DEFAULT_PORT=587
|
||||
MAIL_DEFAULT_ENCRYPTION=tls
|
||||
MAIL_DEFAULT_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_DEFAULT_FROM_NAME="Al Rahma Sunday School"
|
||||
# Optional Reply-To (uncomment if you want a replyable inbox)
|
||||
MAIL_DEFAULT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_DEFAULT_REPLY_TO_NAME="No-Reply"
|
||||
# Optional bounce address (envelope sender)
|
||||
# MAIL_DEFAULT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
# Optional DKIM
|
||||
# MAIL_DEFAULT_DKIM_DOMAIN=alrahmaisgl.org
|
||||
# MAIL_DEFAULT_DKIM_SELECTOR=default
|
||||
# MAIL_DEFAULT_DKIM_PRIVATE=/path/to/private.key
|
||||
|
||||
# ===== COMMUNICATION =====
|
||||
MAIL_COMMUNICATION_HOST=smtp.gmail.com
|
||||
MAIL_COMMUNICATION_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_COMMUNICATION_PASS="psnp emdq dykw ypul"
|
||||
MAIL_COMMUNICATION_PORT=587
|
||||
MAIL_COMMUNICATION_ENCRYPTION=tls
|
||||
MAIL_COMMUNICATION_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_COMMUNICATION_FROM_NAME="School Communications"
|
||||
MAIL_COMMUNICATION_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_COMMUNICATION_REPLY_TO_NAME="School Communications"
|
||||
# MAIL_COMMUNICATION_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ===== PAYMENT =====
|
||||
MAIL_PAYMENT_HOST=smtp.gmail.com
|
||||
MAIL_PAYMENT_USER=alrahma.sunday.school@gmail.com
|
||||
MAIL_PAYMENT_PASS="psnp emdq dykw ypul"
|
||||
MAIL_PAYMENT_PORT=587
|
||||
MAIL_PAYMENT_ENCRYPTION=tls
|
||||
MAIL_PAYMENT_FROM_EMAIL=alrahma.sunday.school@gmail.com
|
||||
MAIL_PAYMENT_FROM_NAME="School Payments"
|
||||
MAIL_PAYMENT_REPLY_TO=alrahma.isgl@gmail.com
|
||||
MAIL_PAYMENT_REPLY_TO_NAME="School Payments"
|
||||
# MAIL_PAYMENT_RETURN_PATH=bounces@alrahmaisgl.org
|
||||
|
||||
# ---- Legacy fallbacks (kept for compatibility) ----
|
||||
SMTP_HOST = smtp.gmail.com
|
||||
SMTP_USER = alrahma.sunday.school@gmail.com
|
||||
SMTP_PASS = "psnp emdq dykw ypul"
|
||||
SMTP_PORT = 587
|
||||
SMTP_ENCRYPTION = tls # was SSL; set to ssl to match 587
|
||||
|
||||
# Optional sender directory you already use elsewhere
|
||||
MAIL_SENDERS="{\"general\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma No-Reply\"},\
|
||||
\"registration\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Register Office\"},\
|
||||
\"notifications\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Notifications\"},\
|
||||
\"finance\":{\"email\":\"alrahma.isgl@gmail.com\",\"name\":\"Al Rahma Finance Office\"}}"
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
app.baseURL = 'https://test.alrahmaisgl.org/'
|
||||
# app.forceGlobalSecureRequests = true
|
||||
# app.CSPEnabled = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
database.default.hostname = localhost
|
||||
database.default.database = u280815660_schooltest
|
||||
database.default.username = u280815660_moulay
|
||||
database.default.password = E3s6F?jn$;
|
||||
database.default.DBDriver = MySQLi
|
||||
database.default.DBPrefix =
|
||||
database.default.port = 3306
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CONTENT SECURITY POLICY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# contentsecuritypolicy.reportOnly = false
|
||||
# contentsecuritypolicy.defaultSrc = 'none'
|
||||
# contentsecuritypolicy.scriptSrc = 'self'
|
||||
# contentsecuritypolicy.styleSrc = 'self'
|
||||
# contentsecuritypolicy.imageSrc = 'self'
|
||||
# contentsecuritypolicy.baseURI = null
|
||||
# contentsecuritypolicy.childSrc = null
|
||||
# contentsecuritypolicy.connectSrc = 'self'
|
||||
# contentsecuritypolicy.fontSrc = null
|
||||
# contentsecuritypolicy.formAction = null
|
||||
# contentsecuritypolicy.frameAncestors = null
|
||||
# contentsecuritypolicy.frameSrc = null
|
||||
# contentsecuritypolicy.mediaSrc = null
|
||||
# contentsecuritypolicy.objectSrc = null
|
||||
# contentsecuritypolicy.pluginTypes = null
|
||||
# contentsecuritypolicy.reportURI = null
|
||||
# contentsecuritypolicy.sandbox = false
|
||||
# contentsecuritypolicy.upgradeInsecureRequests = false
|
||||
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
|
||||
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
|
||||
# contentsecuritypolicy.autoNonce = true
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# COOKIE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# cookie.prefix = ''
|
||||
# cookie.expires = 0
|
||||
# cookie.path = '/'
|
||||
# cookie.domain = ''
|
||||
# cookie.secure = false
|
||||
# cookie.httponly = false
|
||||
# cookie.samesite = 'Lax'
|
||||
# cookie.raw = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENCRYPTION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# encryption.key =
|
||||
# encryption.driver = OpenSSL
|
||||
# encryption.blockSize = 16
|
||||
# encryption.digest = SHA512
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# HONEYPOT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# honeypot.hidden = 'true'
|
||||
# honeypot.label = 'Fill This Field'
|
||||
# honeypot.name = 'honeypot'
|
||||
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
|
||||
# honeypot.container = '<div style="display:none">{template}</div>'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SECURITY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# security.csrfProtection = 'cookie'
|
||||
# security.tokenRandomize = false
|
||||
# security.tokenName = 'csrf_token_name'
|
||||
# security.headerName = 'X-CSRF-TOKEN'
|
||||
# security.cookieName = 'csrf_cookie_name'
|
||||
# security.expires = 7200
|
||||
# security.regenerate = true
|
||||
# security.redirect = false
|
||||
# security.samesite = 'Lax'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SESSION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# session.cookieName = 'ci_session'
|
||||
# session.expiration = 7200
|
||||
# session.savePath = null
|
||||
# session.matchIP = false
|
||||
# session.timeToUpdate = 300
|
||||
# session.regenerateDestroy = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# LOGGER
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# logger.threshold = 4
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CURLRequest
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# curlrequest.shareOptions = false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mail.protocol = smtp
|
||||
mail.SMTPHost = ${SMTP_HOST}
|
||||
mail.SMTPUser = ${SMTP_USER}
|
||||
mail.SMTPPass = ${SMTP_PASS}
|
||||
mail.SMTPPort = ${SMTP_PORT}
|
||||
mail.SMTPCrypto = tls
|
||||
|
||||
mail.fromEmail = ${SMTP_USER}
|
||||
mail.fromName = "Al Rahma Sunday School"
|
||||
mail.mailType = text
|
||||
mail.charset = UTF-8
|
||||
@@ -0,0 +1,190 @@
|
||||
#--------------------------------------------------------------------
|
||||
# Example Environment Configuration file
|
||||
#
|
||||
# This file can be used as a starting point for your own
|
||||
# custom .env files, and contains most of the possible settings
|
||||
# available in a default install.
|
||||
#
|
||||
# By default, all of the settings are commented out. If you want
|
||||
# to override the setting, you must un-comment it by removing the '#'
|
||||
# at the beginning of the line.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
CI_ENVIRONMENT = development
|
||||
|
||||
# Which profile to use if none is supplied to sendEmail()
|
||||
# Options: default, communication, payment, ...
|
||||
MAIL_PROFILE_DEFAULT=default
|
||||
|
||||
# ===== DEFAULT (system) =====
|
||||
# ---- Legacy fallbacks (kept for compatibility) ----
|
||||
SMTP_HOST = smtp.gmail.com
|
||||
SMTP_USER = alrahma.sunday.school@gmail.com
|
||||
SMTP_PASS = "psnp emdq dykw ypul"
|
||||
SMTP_PORT = 587
|
||||
SMTP_ENCRYPTION = tls # was SSL; set to ssl to match 587
|
||||
|
||||
# Optional sender directory you already use elsewhere
|
||||
MAIL_SENDERS="{\"general\":{\"email\":\"alrahma.sunday.school@gmail.com\",\"name\":\"Al Rahma No-Reply\"},\
|
||||
\"registration\":{\"email\":\"alrahma.sunday.school@gmail.com\",\"name\":\"Al Rahma Register Office\"},\
|
||||
\"notifications\":{\"email\":\"alrahma.sunday.school@gmail.com\",\"name\":\"Al Rahma Notifications\"},\
|
||||
\"finance\":{\"email\":\"alrahma.sunday.school@gmail.com\",\"name\":\"Al Rahma Finance Office\"}}"
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
app_baseURL = 'http://localhost:8080/'
|
||||
# app.forceGlobalSecureRequests = true
|
||||
# app.CSPEnabled = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
database.default.hostname = 127.0.0.1
|
||||
database.default.database = school
|
||||
database.default.username = root
|
||||
database.default.password =
|
||||
database.default.DBDriver = MySQLi
|
||||
database.default.DBPrefix =
|
||||
database.default.port = 3306
|
||||
database.default.DBDebug = true
|
||||
database.default.charset = utf8mb4
|
||||
database.default.DBCollat = utf8mb4_unicode_ci # or utf8mb4_general_ci, or on MySQL 8: utf8mb4_0900_ai_ci
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CONTENT SECURITY POLICY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# contentsecuritypolicy.reportOnly = false
|
||||
# contentsecuritypolicy.defaultSrc = 'none'
|
||||
# contentsecuritypolicy.scriptSrc = 'self'
|
||||
# contentsecuritypolicy.styleSrc = 'self'
|
||||
# contentsecuritypolicy.imageSrc = 'self'
|
||||
# contentsecuritypolicy.baseURI = null
|
||||
# contentsecuritypolicy.childSrc = null
|
||||
# contentsecuritypolicy.connectSrc = 'self'
|
||||
# contentsecuritypolicy.fontSrc = null
|
||||
# contentsecuritypolicy.formAction = null
|
||||
# contentsecuritypolicy.frameAncestors = null
|
||||
# contentsecuritypolicy.frameSrc = null
|
||||
# contentsecuritypolicy.mediaSrc = null
|
||||
# contentsecuritypolicy.objectSrc = null
|
||||
# contentsecuritypolicy.pluginTypes = null
|
||||
# contentsecuritypolicy.reportURI = null
|
||||
# contentsecuritypolicy.sandbox = false
|
||||
# contentsecuritypolicy.upgradeInsecureRequests = false
|
||||
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
|
||||
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
|
||||
# contentsecuritypolicy.autoNonce = true
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# COOKIE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# cookie.prefix = ''
|
||||
# cookie.expires = 0
|
||||
# cookie.path = '/'
|
||||
# cookie.domain = ''
|
||||
# cookie.secure = false
|
||||
# cookie.httponly = false
|
||||
# cookie.samesite = 'Lax'
|
||||
# cookie.raw = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# ENCRYPTION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# encryption.key =
|
||||
# encryption.driver = OpenSSL
|
||||
# encryption.blockSize = 16
|
||||
# encryption.digest = SHA512
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# HONEYPOT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# honeypot.hidden = 'true'
|
||||
# honeypot.label = 'Fill This Field'
|
||||
# honeypot.name = 'honeypot'
|
||||
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
|
||||
# honeypot.container = '<div style="display:none">{template}</div>'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SECURITY
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# security.csrfProtection = 'cookie'
|
||||
# security.tokenRandomize = false
|
||||
# security.tokenName = 'csrf_token_name'
|
||||
# security.headerName = 'X-CSRF-TOKEN'
|
||||
# security.cookieName = 'csrf_cookie_name'
|
||||
# security.expires = 7200
|
||||
# security.regenerate = true
|
||||
# security.redirect = false
|
||||
# security.samesite = 'Lax'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SESSION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# session.cookieName = 'ci_session'
|
||||
# session.expiration = 7200
|
||||
# session.savePath = null
|
||||
# session.matchIP = false
|
||||
# session.timeToUpdate = 300
|
||||
# session.regenerateDestroy = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# LOGGER
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# logger.threshold = 4
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CURLRequest
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# curlrequest.shareOptions = false
|
||||
|
||||
|
||||
mail.protocol = smtp
|
||||
mail.SMTPHost = ${SMTP_HOST}
|
||||
mail.SMTPUser = ${SMTP_USER}
|
||||
mail.SMTPPass = ${SMTP_PASS}
|
||||
mail.SMTPPort = ${SMTP_PORT}
|
||||
mail.SMTPCrypto = tls
|
||||
|
||||
mail.fromEmail = ${SMTP_USER}
|
||||
mail.fromName = "Al Rahma Sunday School"
|
||||
mail.mailType = text
|
||||
mail.charset = UTF-8
|
||||
|
||||
|
||||
# ---- Printer Mode (network or usb)
|
||||
PRINTER_MODE=network
|
||||
|
||||
# ---- Network printer settings
|
||||
PRINTER_HOST=192.168.1.100
|
||||
PRINTER_PORT=9100
|
||||
|
||||
# ---- USB printer settings (used when PRINTER_MODE=usb)
|
||||
PRINTER_USB_VID=0x0dd4
|
||||
PRINTER_USB_PID=0x0285
|
||||
PRINTER_WINDOWS_NAME="Custom Engineering SPA Receipt"
|
||||
|
||||
# ---- Formatting
|
||||
# 80mm paper ~ 48-56 characters per line
|
||||
PRINTER_CHARS_PER_LINE=48
|
||||
PRINTER_FEED_LINES=3
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
// Configuration
|
||||
$sqlFile = __DIR__ . '/schema.sql';
|
||||
$migrationPath = __DIR__ . '/app/Database/Migrations/';
|
||||
|
||||
if (!file_exists($sqlFile)) {
|
||||
die("❌ SQL file not found at $sqlFile\n");
|
||||
}
|
||||
|
||||
$sql = file_get_contents($sqlFile);
|
||||
|
||||
// Match CREATE TABLE statements
|
||||
preg_match_all('/CREATE TABLE\s+(?:IF NOT EXISTS\s+)?`?(\w+)`?\s*\((.*?)\)\s*(?:ENGINE|CHARSET|;)/is', $sql, $matches, PREG_SET_ORDER);
|
||||
|
||||
echo "Found " . count($matches) . " table(s).\n";
|
||||
|
||||
foreach ($matches as $match) {
|
||||
$tableName = $match[1];
|
||||
$columnsSql = trim($match[2]);
|
||||
|
||||
$className = 'Create' . ucfirst(camelCase($tableName)) . 'Table';
|
||||
$timestamp = date('YmdHis');
|
||||
$filename = $timestamp . '_create_' . strtolower($tableName) . '_table.php';
|
||||
sleep(1); // Avoid filename collisions
|
||||
|
||||
$columnsArray = parseColumns($columnsSql);
|
||||
$fieldDefs = array_map(
|
||||
fn($name, $def) => is_numeric($name)
|
||||
? " $def,"
|
||||
: " '$name' => $def,",
|
||||
array_keys($columnsArray),
|
||||
$columnsArray
|
||||
);
|
||||
$fieldDefsText = implode("\n", $fieldDefs);
|
||||
|
||||
$migrationContent = <<<PHP
|
||||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class $className extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
\$this->forge->addField([
|
||||
$fieldDefsText
|
||||
]);
|
||||
\$this->forge->addKey('id', true); // Adjust if needed
|
||||
\$this->forge->createTable('$tableName');
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
\$this->forge->dropTable('$tableName');
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
file_put_contents($migrationPath . $filename, $migrationContent);
|
||||
echo "✅ Created migration for `$tableName`: $filename\n";
|
||||
}
|
||||
|
||||
function parseColumns(string $columnsSql): array
|
||||
{
|
||||
$lines = preg_split('/,\n|\n/', $columnsSql);
|
||||
$result = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
|
||||
// Skip keys, constraints
|
||||
if (preg_match('/^(PRIMARY|UNIQUE|KEY|CONSTRAINT|FOREIGN)/i', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle ENUM as raw SQL
|
||||
if (preg_match("/^`(\w+)`\s+enum\((.*?)\)(.*)/i", $line, $enumMatch)) {
|
||||
$name = $enumMatch[1];
|
||||
$values = $enumMatch[2];
|
||||
$rest = $enumMatch[3];
|
||||
|
||||
// Wrap the whole ENUM in double quotes, and escape single quotes
|
||||
$enumSQL = '"' . "$name ENUM($values)$rest" . '"';
|
||||
$enumSQL = str_replace("`", "", $enumSQL);
|
||||
$result[] = $enumSQL;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (preg_match('/^`(\w+)`\s+([a-zA-Z]+)(\(([^)]+)\))?/i', $line, $col)) {
|
||||
$field = $col[1];
|
||||
$type = strtoupper($col[2]);
|
||||
$length = $col[4] ?? null;
|
||||
|
||||
$definition = [];
|
||||
$definition[] = "'type' => '$type'";
|
||||
|
||||
if ($length && !in_array($type, ['TEXT', 'DATE', 'DATETIME', 'TIMESTAMP'])) {
|
||||
$definition[] = "'constraint' => $length";
|
||||
}
|
||||
|
||||
$definition[] = stripos($line, 'NOT NULL') !== false ? "'null' => false" : "'null' => true";
|
||||
|
||||
if (stripos($line, 'AUTO_INCREMENT') !== false) {
|
||||
$definition[] = "'auto_increment' => true";
|
||||
}
|
||||
|
||||
if (preg_match('/DEFAULT\s+([^\s,]+)/i', $line, $defMatch)) {
|
||||
$default = trim($defMatch[1], "'\"");
|
||||
if (strtoupper($default) !== 'NULL') {
|
||||
$definition[] = "'default' => '$default'";
|
||||
}
|
||||
}
|
||||
|
||||
$result[$field] = '[' . implode(', ', $definition) . ']';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function camelCase(string $str): string
|
||||
{
|
||||
return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $str)));
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# Disable directory browsing
|
||||
Options -Indexes
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Rewrite engine
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Turning on the rewrite engine is necessary for the following rules and features.
|
||||
# FollowSymLinks must be enabled for this to work.
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks
|
||||
RewriteEngine On
|
||||
|
||||
# If you installed CodeIgniter in a subfolder, you will need to
|
||||
# change the following line to match the subfolder you need.
|
||||
# http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
|
||||
#RewriteBase /alrahma/
|
||||
|
||||
# Redirect Trailing Slashes...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Rewrite "www.example.com" and "https://test.alrahmaisgl.org" to "https://example.com"
|
||||
RewriteCond %{HTTPS} off [OR]
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# Masking URL: Redirect all requests to a specific page while keeping the URL visible in the address bar
|
||||
# Example: Mask the actual URL path and display "/masked-url" in the address bar
|
||||
RewriteRule ^masked-url$ /actual-path/to/page.php [L]
|
||||
|
||||
# Checks to see if the user is attempting to access a valid file,
|
||||
# such as an image or CSS document; if this isn't true it sends the
|
||||
# request to the front controller, index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA]
|
||||
|
||||
# Ensure Authorization header is passed along
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
ErrorDocument 404 index.php
|
||||
</IfModule>
|
||||
|
||||
# Disable server signature start
|
||||
ServerSignature Off
|
||||
# Disable server signature end
|
||||
@@ -0,0 +1,254 @@
|
||||
Grade,Unit,Unit Title,chapter
|
||||
1,1,Aqaid: Our Belief,1. Allah: Our Creator
|
||||
1,1,Aqaid: Our Belief,2. Islam
|
||||
1,1,Aqaid: Our Belief,3. Our Faith
|
||||
1,1,Aqaid: Our Belief,4. Nabi Muhammad (s)
|
||||
1,1,Aqaid: Our Belief,5. The Qur’an
|
||||
1,2,Knowing Allah,6. Allah Loves Us
|
||||
1,2,Knowing Allah,7. Remembering Allah
|
||||
1,2,Knowing Allah,8. Allah Rewards Us
|
||||
1,3,Our Ibadat,9. Five Pillars of Islam
|
||||
1,3,Our Ibadat,10. Shahadah: The First Pillar
|
||||
1,3,Our Ibadat,11. Salat: The Second Pillar
|
||||
1,3,Our Ibadat,12. Zakat: The Third Pillar
|
||||
1,3,Our Ibadat,13. Fasting: The Fourth Pillar
|
||||
1,3,Our Ibadat,14. Hajj: The Fifth Pillar
|
||||
1,4,Messengers of Allah,15. Adam (A): The First Nabi
|
||||
1,4,Messengers of Allah,16. Nuh (A): Saved From the Great Flood
|
||||
1,4,Messengers of Allah,17. Ibrahim (A): Never Listen to Shaitan
|
||||
1,4,Messengers of Allah,18. Musa (A): Challenging a Bad Ruler
|
||||
1,4,Messengers of Allah,19. Isa (A): A Great Nabi of Allah
|
||||
1,5,Other Basics of Islam,20. Angels: They Always Work for Allah
|
||||
1,5,Other Basics of Islam,21. Shaitan: Our Enemy
|
||||
1,5,Other Basics of Islam,22. Makkah and Madinah
|
||||
1,5,Other Basics of Islam,23. Eid: Two Festivals
|
||||
1,6,Akhlaq and Adab in Islam,24. Good Manners
|
||||
1,6,Akhlaq and Adab in Islam,25. Kindness and Sharing
|
||||
1,6,Akhlaq and Adab in Islam,26. Respect
|
||||
1,6,Akhlaq and Adab in Islam,27. Forgiveness
|
||||
1,6,Akhlaq and Adab in Islam,28. Thanking Allah
|
||||
2,1,The Creator and His Message,1. Allah: Our Creator
|
||||
2,1,The Creator and His Message,2. How Does Allah Create?
|
||||
2,1,The Creator and His Message,3. What Does Allah Do?
|
||||
2,1,The Creator and His Message,4, Allah: What Does He Not Do
|
||||
2,1,The Creator and His Message,5. The Qur’an
|
||||
2,1,The Creator and His Message,6. Hadith and Sunnah
|
||||
2,2,Our Ibadat,7. Shahadah: The First Pillar
|
||||
2,2,Our Ibadat,8. Salat: The Second Pillar
|
||||
2,2,Our Ibadat,9. Zakah: The Third Pillar
|
||||
2,2,Our Ibadat,10. Sawm: The Fourth Pillar
|
||||
2,2,Our Ibadat,11. Hajj: The Fifth Pillar
|
||||
2,2,Our Ibadat,12. Wudu: Cleaning Before Salat
|
||||
2,3,The Messengers of Allah,13. Ibrahim (A): A Friend of Allah
|
||||
2,3,The Messengers of Allah,14. Yaqub (A) and Yusuf (A)
|
||||
2,3,The Messengers of Allah,15. Musa (A) and Harun (A)
|
||||
2,3,The Messengers of Allah,16. Yunus (A)
|
||||
2,3,The Messengers of Allah,17. Nabi Muhammad ﷺ
|
||||
2,4,Learning About Islam,18. Obey Allah, Obey Rasul ﷺ
|
||||
2,4,Learning About Islam,19. Day of Judgment
|
||||
2,4,Learning About Islam,20. Our Masjid
|
||||
2,4,Learning About Islam,21. Islamic Phrases
|
||||
2,4,Learning About Islam,22. Food that We May Eat
|
||||
2,5,Akhlaq and Adab in Islam,23. Truthfulness
|
||||
2,5,Akhlaq and Adab in Islam,24. Kindness
|
||||
2,5,Akhlaq and Adab in Islam,25. Respect
|
||||
2,5,Akhlaq and Adab in Islam,26. Responsibility
|
||||
2,5,Akhlaq and Adab in Islam,27. Obedience
|
||||
2,5,Akhlaq and Adab in Islam,28. Cleanliness
|
||||
2,5,Akhlaq and Adab in Islam,29. Honesty
|
||||
3,1,Knowing About Allāh ﷺ,1. Who Is Allāh ﷺ?
|
||||
3,1,Knowing About Allāh ﷺ,2. What Allāh ﷺ Is and Is Not
|
||||
3,1,Knowing About Allāh ﷺ,3. Allāh ﷺ: The Most-Merciful, Most-Rewarding
|
||||
3,1,Knowing About Allāh ﷺ,4. Allāh ﷺ: The Best Judge
|
||||
3,1,Knowing About Allāh ﷺ,5. What Does Allāh ﷺ Want Us to Do?
|
||||
3,2,Teachings of Islam,6. We Are Muslims: We Have ‘Īmān
|
||||
3,2,Teachings of Islam,7. Belief in the Qur’ān
|
||||
3,2,Teachings of Islam,8. Belief in the Messengers
|
||||
3,2,Teachings of Islam,9. Hadīth and Sunnah
|
||||
3,2,Teachings of Islam,10. Jinn
|
||||
3,2,Teachings of Islam,11. Muslims in North America
|
||||
3,2,Teachings of Islam,12. The Straight Path: The Right Path
|
||||
3,3,Nabi Muhammad ﷺ,13. Kindness of Rasūlullāh ﷺ
|
||||
3,3,Nabi Muhammad ﷺ,14. How Rasūlullāh ﷺ Treated Others
|
||||
3,3,Nabi Muhammad ﷺ,15. Our Relationship With Rasūlullāh ﷺ
|
||||
3,4,Messengers of Allāh ﷺ,16. Ismā‘īl (A) and Ishāq (A): Nabi of Allāh ﷺ
|
||||
3,4,Messengers of Allāh ﷺ,17. Shu‘aib (A): A Nabi of Allāh ﷺ
|
||||
3,4,Messengers of Allāh ﷺ,18. Dāwūd (A): A Nabi of Allāh ﷺ
|
||||
3,4,Messengers of Allāh ﷺ,19. ‘Īsā (A): A Nabi of Allāh ﷺ
|
||||
3,5,Learning About Islam,20. The Ka‘bah
|
||||
3,5,Learning About Islam,21. Masjid an-Nabawī: The Nabi’s Masjid
|
||||
3,5,Learning About Islam,22. Bilāl ibn Rabāh
|
||||
3,5,Learning About Islam,23. Zaid ibn Hārithah
|
||||
3,6,Akhlaq and Adab in Islam,24. Ways To Be a Good Person
|
||||
3,6,Akhlaq and Adab in Islam,25. Kindness: A Virtue of the Believers
|
||||
3,6,Akhlaq and Adab in Islam,26. Forgiveness: A Quality of the Believers
|
||||
3,6,Akhlaq and Adab in Islam,27. Good Deeds: A Duty of the Believers
|
||||
3,6,Akhlaq and Adab in Islam,28. Perseverance: Never Give Up
|
||||
3,6,Akhlaq and Adab in Islam,29. Punctuality: Doing Things on Time
|
||||
4,1,Knowing the Creator,1. Rewards of Allah: Everybody Receives Them
|
||||
4,1,Knowing the Creator,1. Discipline of Allah: Because He Loves Us
|
||||
4,1,Knowing the Creator,3. Names of Allah
|
||||
4,1,Knowing the Creator,4. Books of Allah
|
||||
4,2,How Islam Changed Arabia,5. Pre-Islamic Arabia: Age of Ignorance
|
||||
4,2,How Islam Changed Arabia,6. The Year of the Elephant
|
||||
4,2,How Islam Changed Arabia,7. Early Life of Muhammad ﷺ
|
||||
4,2,How Islam Changed Arabia,8. Life Before Becoming a Nabi
|
||||
4,2,How Islam Changed Arabia,9. First Revelation
|
||||
4,2,How Islam Changed Arabia,10. Makkah Period: The Early Years of the Muslims
|
||||
4,2,How Islam Changed Arabia,11. Hijrat to Madinah: The Migration that Shaped History
|
||||
4,2,How Islam Changed Arabia,12. Madinah Period: Islam Prospers
|
||||
4,3,The Rightly Guided Khalifah,13. Abū Bakr (R): The First Khalifah
|
||||
4,3,The Rightly Guided Khalifah,14. ‘Umar al-Khaṭṭāb (R): The Second Khalifah
|
||||
4,3,The Rightly Guided Khalifah,15. ‘Uthman Ibn ‘Affān (R): The Third Khalifah
|
||||
4,3,The Rightly Guided Khalifah,16. ‘Ali Ibn Abu Ṭālib (R): The Fourth Khalifah
|
||||
4,4,Messengers of Allah,17. Hūd (A): Struggle to Guide Mankind
|
||||
4,4,Messengers of Allah,18. Ṣāliḥ (A): Struggle to Guide the Misguided
|
||||
4,4,Messengers of Allah,19. Mūsā (A): His Life and Achievements
|
||||
4,4,Messengers of Allah,20. Sulaimān (A): A King and a Servant of Allah ﷺ
|
||||
4,5,Fiqh of Salat,21. Preparation for Salat
|
||||
4,5,Fiqh of Salat,22. The Requirements of Salat
|
||||
4,5,Fiqh of Salat,23. Mubṭilāt-us-Salāt: Things that Invalidate Salāt
|
||||
4,5,Fiqh of Salat,24. How to Pray Behind an Imām
|
||||
4,6,General Islamic Topics,25. Compilers of Hadīth
|
||||
4,6,General Islamic Topics,26. Shaitan’s Mode of Operation
|
||||
4,6,General Islamic Topics,27. Day of Judgment: The Day of Ultimate Justice
|
||||
4,6,General Islamic Topics,28. ‘Eid: Significance of the Festivities
|
||||
4,6,General Islamic Topics,29. Truthfulness: An Important Quality for Muslims
|
||||
4,6,General Islamic Topics,30. Perseverance: Keep on Trying
|
||||
5,1,The Creator,1. His Message, and His Messengers,Tawhid, Kafir, Kufr, Shirk, Nifaq
|
||||
5,1,The Creator,2. His Message, and His Messengers,Why Should We Worship Allah?
|
||||
5,1,The Creator,3. His Message, and His Messengers,The Revelation of the Qur’an
|
||||
5,1,The Creator,4. His Message, and His Messengers,Characteristics of the Messengers
|
||||
5,2,The Battles and Other Developments,5. Pledges of ‘Aqabah: Invitation to Migrate
|
||||
5,2,The Battles and Other Developments,6. The Battle of Badr: Allah Supports the Righteous
|
||||
5,2,The Battles and Other Developments,7. The Battle of Uhud: Obey Allah and Obey the Rasul ﷺ
|
||||
5,2,The Battles and Other Developments,8. The Battle of the Trench: A Bloodless Battle
|
||||
5,2,The Battles and Other Developments,9. The Treaty of Hudaibiyah: A Clear Victory
|
||||
5,2,The Battles and Other Developments,10. Liberation of Makkah: A Bloodless Victory
|
||||
5,3,Stories of the Messengers of Allah,11. Adam (A): The Creation of Human Beings
|
||||
5,3,Stories of the Messengers of Allah,12. Ibrahim (A): His Debate with the Polytheists
|
||||
5,3,Stories of the Messengers of Allah,13. Ibrahim (A): His Plan Against the Idols
|
||||
5,3,Stories of the Messengers of Allah,14. Luqmān (A): A Wise Man’s Lifelong Advice
|
||||
5,3,Stories of the Messengers of Allah,15. Yūsuf (A): His Childhood and Life in Aziz’s Home
|
||||
5,3,Stories of the Messengers of Allah,16. Yūsuf (A): Standing Up for Righteousness
|
||||
5,3,Stories of the Messengers of Allah,17. Yūsuf (A): A Childhood Dream Comes True
|
||||
5,4,Islam in The World,20. Major Masājid in the World
|
||||
5,5,Islamic Values and Teachings,21. Upholding Truth: A Duty of All Believers
|
||||
5,5,Islamic Values and Teachings,22. Responsibility and Punctuality
|
||||
5,5,Islamic Values and Teachings,23. My Mind, My Body: The Body is a Mirror of the Mind
|
||||
5,5,Islamic Values and Teachings,24. Kindness and Forgiveness
|
||||
5,5,Islamic Values and Teachings,25. The Middle Path: Ways to Avoid the Two Extremes
|
||||
5,5,Islamic Values and Teachings,26. Salat: Its Significance
|
||||
5,5,Islamic Values and Teachings,27. Sawm: Its Significance
|
||||
5,5,Islamic Values and Teachings,28. Zakat and Sadaqah: Similarities and Differences
|
||||
6,1,The Creator,1. His Message, and His Messengers,Tawhid, Kafir, Kufr, Shirk, Nifaq
|
||||
6,1,The Creator,2. His Message, and His Messengers,Why Should We Worship Allah?
|
||||
6,1,The Creator,3. His Message, and His Messengers,The Revelation of the Qur’an
|
||||
6,1,The Creator,4. His Message, and His Messengers,Characteristics of the Messengers
|
||||
6,2,The Battles and Other Developments,5. Pledges of ‘Aqabah: Invitation to Migrate
|
||||
6,2,The Battles and Other Developments,6. The Battle of Badr: Allah Supports the Righteous
|
||||
6,2,The Battles and Other Developments,7. The Battle of Uhud: Obey Allah and Obey the Rasul ﷺ
|
||||
6,2,The Battles and Other Developments,8. The Battle of the Trench: A Bloodless Battle
|
||||
6,2,The Battles and Other Developments,9. The Treaty of Hudaibiyah: A Clear Victory
|
||||
6,2,The Battles and Other Developments,10. Liberation of Makkah: A Bloodless Victory
|
||||
6,3,Stories of the Messengers of Allah,11. Adam (A): The Creation of Human Beings
|
||||
6,3,Stories of the Messengers of Allah,12. Ibrahim (A): His Debate with the Polytheists
|
||||
6,3,Stories of the Messengers of Allah,13. Ibrahim (A): His Plan Against the Idols
|
||||
6,3,Stories of the Messengers of Allah,14. Luqmān (A): A Wise Man’s Lifelong Advice
|
||||
6,3,Stories of the Messengers of Allah,15. Yūsuf (A): His Childhood and Life in Aziz’s Home
|
||||
6,3,Stories of the Messengers of Allah,16. Yūsuf (A): Standing Up for Righteousness
|
||||
6,3,Stories of the Messengers of Allah,17. Yūsuf (A): A Childhood Dream Comes True
|
||||
6,4,Islam in The World,20. Major Masājid in the World
|
||||
6,5,Islamic Values and Teachings,21. Upholding Truth: A Duty of All Believers
|
||||
6,5,Islamic Values and Teachings,22. Responsibility and Punctuality
|
||||
6,5,Islamic Values and Teachings,23. My Mind, My Body: The Body is a Mirror of the Mind
|
||||
6,5,Islamic Values and Teachings,24. Kindness and Forgiveness
|
||||
6,5,Islamic Values and Teachings,25. The Middle Path: Ways to Avoid the Two Extremes
|
||||
6,5,Islamic Values and Teachings,26. Salat: Its Significance
|
||||
6,5,Islamic Values and Teachings,27. Sawm: Its Significance
|
||||
6,5,Islamic Values and Teachings,28. Zakat and Sadaqah: Similarities and Differences
|
||||
7,1,The Creator,1. Why Islam? What is Islam?
|
||||
7,1,The Creator,2. Belief in Allah
|
||||
7,1,The Creator,3. The Qur’an: Its Qualitative Names
|
||||
7,1,The Creator,4. Istighfār: Seeking Forgiveness and Protection
|
||||
7,1,The Creator,5. Allah: Angry or Kind?
|
||||
7,2,Stories of the Messengers,6. Ādam (A): The Trial of the First Messenger
|
||||
7,2,Stories of the Messengers,7. The Life of Ibrāhīm (A): Beginning a Nation
|
||||
7,2,Stories of the Messengers,8. The Sacrifice of Ibrāhīm (A)
|
||||
7,2,Stories of the Messengers,9. Lūt (A): A Message for Modern Societies
|
||||
7,2,Stories of the Messengers,10. Yūsuf (A): The Will to Overcome Temptation
|
||||
7,3,Stories from the Qur’an,11. The Companions of the Cave
|
||||
7,3,Stories from the Qur’an,12. Dhu al-Qarnain: The Journey of a King
|
||||
7,3,Stories from the Qur’an,13. Effective Debate and Negotiation Styles in the Qur’an
|
||||
7,4,Two Companions Who Shaped Islam,14. Abū Sufyān: His Life and Achievements
|
||||
7,4,Two Companions Who Shaped Islam,15. Khālid Ibn al-Walīd: The “Sword of Allah”
|
||||
7,5,Knowledge Enrichment,16. Character of the Messengers
|
||||
7,5,Knowledge Enrichment,17. Rasūlullāh’s Marriages
|
||||
7,5,Knowledge Enrichment,18. Lailatul Qadr: The Night of Majesty
|
||||
7,5,Knowledge Enrichment,19. Fasting During Ramadan: The Month of Benefits
|
||||
7,5,Knowledge Enrichment,20. My Family is Muslim Now
|
||||
7,5,Knowledge Enrichment,21. Science in the Qur’an
|
||||
7,5,Knowledge Enrichment,22. Lessons From Past Civilizations
|
||||
7,6,Akhlaq and Adab in Islam,23. Amr Bil Ma’rūf: Enjoin Good Deeds
|
||||
7,6,Akhlaq and Adab in Islam,24. Guard Your Tongue: Think Before You Speak
|
||||
7,6,Akhlaq and Adab in Islam,25. Islamic Greeting: Wishing Peace
|
||||
7,6,Akhlaq and Adab in Islam,26. How to Achieve Success
|
||||
7,6,Akhlaq and Adab in Islam,27. Permitted and Prohibited
|
||||
7,6,Akhlaq and Adab in Islam,28. Types of Behavior Allah Loves
|
||||
8,1,Knowing the Creator,1. Divine Names
|
||||
8,1,Knowing the Creator,2. Sunan of Allah
|
||||
8,1,Knowing the Creator,3. Objectives of the Qur’an
|
||||
8,1,Knowing the Creator,4. Lessons from Sūrah al-Hujurāt
|
||||
8,1,Knowing the Creator,5. True Piety: A Synthesis of Belief, Practice, and Conduct
|
||||
8,1,Knowing the Creator,6. Āyatul Kursi: The Throne Verse
|
||||
8,2,Knowing the Messenger ﷺ,7. The Person Muhammad ﷺ
|
||||
8,2,Knowing the Messenger ﷺ,8. Farewell Pilgrimage
|
||||
8,2,Knowing the Messenger ﷺ,9. Finality of Prophethood
|
||||
8,2,Knowing the Messenger ﷺ,10. Hadith: Collection and Classification
|
||||
8,3,Challenges in Madinah,11. Hypocrites
|
||||
8,3,Challenges in Madinah,12. Banu Qaynuqa: Threat Within Madinah
|
||||
8,3,Challenges in Madinah,13. Banu Nadir: Treachery Within Madinah
|
||||
8,3,Challenges in Madinah,14. Banu Qurayzah
|
||||
8,3,Challenges in Madinah,15. Mission to Tabūk: A Test of Steadfastness
|
||||
8,4,Islamic Ethical Framework,16. Friends and Friendship: Who is a Good Friend?
|
||||
8,4,Islamic Ethical Framework,17. Friendship With Non-Muslims
|
||||
8,4,Islamic Ethical Framework,18. Dating: How Islam Views the Practice
|
||||
8,4,Islamic Ethical Framework,19. Hold Firmly the Rope of Allah
|
||||
8,4,Islamic Ethical Framework,20. Elements of a Bad Life
|
||||
8,5,Islamic Values and Teachings,21. Duties Towards Parents
|
||||
8,5,Islamic Values and Teachings,22. Hope, Hopefulness, Hopelessness
|
||||
8,5,Islamic Values and Teachings,23. Trials in Life: Everyone Will Experience Them
|
||||
8,5,Islamic Values and Teachings,24. Permitted and Prohibited Food
|
||||
8,5,Islamic Values and Teachings,25. Performance of Hajj
|
||||
8,5,Islamic Values and Teachings,26. Parables in the Qur’an
|
||||
8,6,Islam After the Messenger ﷺ,27. Early History of Shi‘ah Muslims
|
||||
8,6,Islam After the Messenger ﷺ,28. Umayyad Dynasty
|
||||
8,6,Islam After the Messenger ﷺ,29. Abbasid Dynasty
|
||||
9,1,A Reflection on the Divine,1. Signs of Allahﷻ in Nature
|
||||
9,1,A Reflection on the Divine,2. Pondering the Qur’an
|
||||
9,1,A Reflection on the Divine,3. Preservation and Compilation of the Qur’an
|
||||
9,1,A Reflection on the Divine,4. Ibadat—Easy Ways to Do It
|
||||
9,1,A Reflection on the Divine,5. Surah Baqarah—Statement of Faith and Commitment
|
||||
9,2,Islam and Muslim,6. Why Human Beings Are Superior
|
||||
9,2,Islam and Muslim,7. Life Cycle of Truth
|
||||
9,2,Islam and Muslim,8. Is Islam a Violent Religion?
|
||||
9,2,Islam and Muslim,9. Present Life: Vanity, Deception, Play
|
||||
9,2,Islam and Muslim,10. Shariah
|
||||
9,2,Islam and Muslim,11. Justice in Islam
|
||||
9,3,Ethical Standard in Islam,12. Choices We Make
|
||||
9,3,Ethical Standard in Islam,13. Peer Pressure
|
||||
9,3,Ethical Standard in Islam,14. Islamic Perspective on Dating
|
||||
9,3,Ethical Standard in Islam,15. Indecency
|
||||
9,3,Ethical Standard in Islam,16. Alcohol and Gambling
|
||||
9,3,Ethical Standard in Islam,17. Permitted and Prohibited Food
|
||||
9,3,Ethical Standard in Islam,18. Food of the People of the Book
|
||||
9,3,Ethical Standard in Islam,19. Let Ramadan Bring The Best in Us
|
||||
9,4,Essays on Rasulullahﷺ,20. Khadijah (ra)
|
||||
9,4,Essays on Rasulullahﷺ,21. Rasulullahﷺ Multiple Marriages
|
||||
9,4,Essays on Rasulullahﷺ,22. Marriage to Zainab (ra)
|
||||
9,4,Essays on Rasulullahﷺ,23. Rasulullahﷺ: A Great Army General
|
||||
9,4,Essays on Rasulullahﷺ,24. Prophecy of Muhammadﷺ in the Bible
|
||||
9,4,Essays on Rasulullahﷺ,25. Allegations Against Rasulullahﷺ
|
||||
9,5,Faith-Based Wealth Building,26. Faith Based Wealth Building
|
||||
9,5,Faith-Based Wealth Building,27. Earn, Save, Spend, Invest
|
||||
9,5,Faith-Based Wealth Building,28. Let Investment Work for You
|
||||
|
@@ -0,0 +1,93 @@
|
||||
<!-- NEW SECTION -->
|
||||
<div class="row">
|
||||
<!-- Medical Conditions -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Medical Conditions (Select all that apply from the 33 items below) <span class="text-danger">*</span></label>
|
||||
<div class="border rounded p-2 bg-white ps-3" style="max-height: 200px; overflow-y: auto;">
|
||||
<?php
|
||||
$medicalOptions = [
|
||||
"None",
|
||||
"ADHD (Attention-Deficit/Hyperactivity Disorder)",
|
||||
"Anxiety or Emotional Disorders",
|
||||
"Asthma",
|
||||
"Autism Spectrum Disorder (ASD)",
|
||||
"Behavioral or Conduct Disorders",
|
||||
"Blindness / Vision Impairment",
|
||||
"Celiac Disease (Gluten Intolerance)",
|
||||
"Cerebral Palsy",
|
||||
"Cystic Fibrosis",
|
||||
"Depression",
|
||||
"Diabetes (Type 1 or Type 2)",
|
||||
"Down Syndrome",
|
||||
"Dyslexia or Learning Disabilities",
|
||||
"Eating Disorders",
|
||||
"Eczema / Severe Skin Conditions",
|
||||
"Epilepsy / Seizure Disorders",
|
||||
"Hearing Impairments / Deafness",
|
||||
"Heart Conditions (congenital or acquired)",
|
||||
"Hemophilia / Bleeding Disorders",
|
||||
"Kidney Disease",
|
||||
"Migraines / Chronic Headaches",
|
||||
"Obsessive-Compulsive Disorder (OCD)",
|
||||
"Physical Disabilities / Mobility Impairments",
|
||||
"PTSD (Post-Traumatic Stress Disorder)",
|
||||
"Sickle Cell Anemia",
|
||||
"Speech and Language Disorders",
|
||||
"Thyroid Disorders",
|
||||
"Tourette Syndrome",
|
||||
"Traumatic Brain Injury (TBI)",
|
||||
"Rheumatic diseases",
|
||||
"Ulcerative Colitis / Crohn’s Disease",
|
||||
"Other"
|
||||
];
|
||||
foreach ($medicalOptions as $opt): ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="medical_conditions[][]" value="<?= esc($opt) ?>" data-base-name="medical_conditions">
|
||||
<label class="form-check-label ms-1"><?= esc($opt) ?></label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<input type="text" class="form-control mt-2 d-none medical-condition-other" name="medical_condition_other[]" placeholder="Please specify if 'Other' selected">
|
||||
</div>
|
||||
|
||||
<!-- Allergies -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Allergies (Select all that apply from the 24 items below) <span class="text-danger">*</span></label>
|
||||
<div class="border rounded p-2 bg-white ps-3" style="max-height: 200px; overflow-y: auto; overflow-x: hidden;">
|
||||
<?php
|
||||
$allergyOptions = [
|
||||
"None",
|
||||
"Animal Dander (cats, dogs, etc.)",
|
||||
"Antibiotics",
|
||||
"Bee stings",
|
||||
"Cockroach",
|
||||
"Corn",
|
||||
"Dust Mites",
|
||||
"Egg",
|
||||
"Fire ant stings",
|
||||
"Fish",
|
||||
"Fragrances / Perfumes",
|
||||
"Latex",
|
||||
"Milk / Dairy",
|
||||
"Mold",
|
||||
"Mosquito bites",
|
||||
"Peanut",
|
||||
"Pollen (grass, tree, weed)",
|
||||
"Sesame",
|
||||
"Shellfish (shrimp, crab, lobster, etc.)",
|
||||
"Soy",
|
||||
"Tree Nuts (almond, cashew, walnut, etc.)",
|
||||
"Wasp stings",
|
||||
"Wheat / Gluten",
|
||||
"Other"
|
||||
];
|
||||
foreach ($allergyOptions as $opt): ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="allergies[][]" value="<?= esc($opt) ?>" data-base-name="allergies">
|
||||
<label class="form-check-label ms-1"><?= esc($opt) ?></label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<input type="text" class="form-control mt-2 d-none allergy-other" name="allergy_other[]" placeholder="Please specify if 'Other' selected">
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,139 @@
|
||||
AdditionalChargeModel.php
|
||||
AdminNotificationSubjectModel.php
|
||||
ApplicationModel.php
|
||||
AttendanceCommentTemplateModel.php
|
||||
AttendanceDataModel.php
|
||||
AttendanceDayModel.php
|
||||
AttendanceEmailTemplateModel.php
|
||||
AttendanceRecordModel.php
|
||||
AttendanceTrackingModel.php
|
||||
AuthorizedUserModel.php
|
||||
BadgePrintLogModel.php
|
||||
BelowSixtyDecisionModel.php
|
||||
CalendarModel.php
|
||||
CertificateRecordModel.php
|
||||
ClassModel.php
|
||||
ClassPrepAdjustmentModel.php
|
||||
ClassPreparationLogModel.php
|
||||
ClassProgressAttachmentModel.php
|
||||
ClassProgressReportModel.php
|
||||
ClassSectionModel.php
|
||||
CommunicationLogModel.php
|
||||
CompetitionClassWinnerModel.php
|
||||
CompetitionModel.php
|
||||
CompetitionScoreModel.php
|
||||
CompetitionWinnerModel.php
|
||||
ConfigurationModel.php
|
||||
ContactUsModel.php
|
||||
CurrentFlagModel.php
|
||||
DiscountUsageModel.php
|
||||
DiscountVoucherModel.php
|
||||
EarlyDismissalSignatureModel.php
|
||||
EmailTemplateModel.php
|
||||
EmergencyContactModel.php
|
||||
EnrollmentAgeRuleModel.php
|
||||
EnrollmentEmailRecordModel.php
|
||||
EnrollmentExceptionModel.php
|
||||
EnrollmentFlagModel.php
|
||||
EnrollmentModel.php
|
||||
EnrollmentTransitionAuditModel.php
|
||||
EventChargesModel.php
|
||||
EventModel.php
|
||||
ExamDraftModel.php
|
||||
ExamModel.php
|
||||
ExpenseModel.php
|
||||
FamilyCommPrefModel.php
|
||||
FamilyGuardianModel.php
|
||||
FamilyModel.php
|
||||
FamilyStudentModel.php
|
||||
FinalExamModel.php
|
||||
FinalScoreModel.php
|
||||
FinancialAidEstimateModel.php
|
||||
FinancialAidRequestModel.php
|
||||
FlagModel.php
|
||||
GradingLockModel.php
|
||||
HomeworkModel.php
|
||||
InventoryCategoryModel.php
|
||||
InventoryItemModel.php
|
||||
InventoryItemYearModel.php
|
||||
InventoryMovementModel.php
|
||||
InvoiceEventModel.php
|
||||
InvoiceModel.php
|
||||
InvoiceStudentListModel.php
|
||||
IpAttemptModel.php
|
||||
JobPositionModel.php
|
||||
JobTemplateModel.php
|
||||
JobTemplateVersionModel.php
|
||||
LateSlipLogModel.php
|
||||
LoginActivityModel.php
|
||||
ManualPaymentModel.php
|
||||
MessageModel.php
|
||||
MidtermExamModel.php
|
||||
MissingScoreOverrideModel.php
|
||||
NavItemModel.php
|
||||
NotificationModel.php
|
||||
ParentAttendanceReportModel.php
|
||||
ParentMeetingScheduleModel.php
|
||||
ParentModel.php
|
||||
ParentNotificationModel.php
|
||||
ParentPolicyAcceptanceModel.php
|
||||
ParticipationModel.php
|
||||
PasswordResetModel.php
|
||||
PasswordResetRequestModel.php
|
||||
PaymentCorrectionModel.php
|
||||
PaymentErrorModel.php
|
||||
PaymentModel.php
|
||||
PaymentNotificationLogModel.php
|
||||
PaymentTransactionModel.php
|
||||
PermissionModel.php
|
||||
PlacementBatchModel.php
|
||||
PlacementLevelModel.php
|
||||
PlacementScoreModel.php
|
||||
PreferencesModel.php
|
||||
PrintRequestModel.php
|
||||
ProjectModel.php
|
||||
PromotionQueueModel.php
|
||||
PurchaseOrderItemModel.php
|
||||
PurchaseOrderModel.php
|
||||
QuizModel.php
|
||||
RefundModel.php
|
||||
RefundPayoutModel.php
|
||||
ReimbursementBatchAdminFileModel.php
|
||||
ReimbursementBatchItemModel.php
|
||||
ReimbursementBatchModel.php
|
||||
ReimbursementModel.php
|
||||
ReportCardAcknowledgementModel.php
|
||||
RoleModel.php
|
||||
RoleNavItemModel.php
|
||||
RolePermissionModel.php
|
||||
SchoolYearClosingBatchModel.php
|
||||
SchoolYearClosingItemModel.php
|
||||
SchoolYearModel.php
|
||||
SchoolYearTransitionLogModel.php
|
||||
ScoreCommentModel.php
|
||||
SectionModel.php
|
||||
SemesterScoreModel.php
|
||||
SettingsModel.php
|
||||
StaffAttendanceModel.php
|
||||
StaffModel.php
|
||||
StudentAllergyModel.php
|
||||
StudentBookIssueModel.php
|
||||
StudentClassModel.php
|
||||
StudentDecisionModel.php
|
||||
StudentMedicalConditionModel.php
|
||||
StudentModel.php
|
||||
StudentSectionDistributionDraftModel.php
|
||||
StudentYearStatusModel.php
|
||||
SubjectCurriculumModel.php
|
||||
SupplyCategoryModel.php
|
||||
TeacherClassModel.php
|
||||
TeacherModel.php
|
||||
TeacherSubmissionNotificationHistoryModel.php
|
||||
UserAccessProfileModel.php
|
||||
UserModel.php
|
||||
UserNotificationModel.php
|
||||
UserRoleModel.php
|
||||
WhatsappGroupLinkModel.php
|
||||
WhatsappGroupMembershipModel.php
|
||||
WhatsappInviteLogModel.php
|
||||
WithdrawalFinancialCalculationModel.php
|
||||
@@ -0,0 +1,492 @@
|
||||
# Payment Data Repair Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This plan fixes the payment table, invoice/payment display logic, and manual payment write flow.
|
||||
|
||||
Reviewed inputs:
|
||||
|
||||
- `invoices(2).sql`
|
||||
- `payments(1).sql`
|
||||
- `PaymentController(2).php`
|
||||
- `PaymentTransactionController(1).php`
|
||||
|
||||
## Current findings
|
||||
|
||||
| Check | Result |
|
||||
|---|---:|
|
||||
| Invoice rows | 126 |
|
||||
| Payment rows | 321 |
|
||||
| Invoice `paid_amount` vs `SUM(payments.paid_amount)` mismatches | 0 |
|
||||
| Payment rows where `payments.total_amount` differs from linked invoice total | 106 |
|
||||
| Invoices affected by stale `payments.total_amount` | 66 |
|
||||
| Payment rows with negative `payments.balance` | 5 |
|
||||
| Payment rows whose `semester` / `school_year` differs from linked invoice | 26 |
|
||||
| Invoice IDs with no payment rows | 46, 76, 77, 108, 112, 122 |
|
||||
|
||||
Main conclusion: payment rows are not missing. The system is reading and storing payment context inconsistently. The payment table is being treated as both a transaction ledger and a cached invoice snapshot, which is why the UI looks wrong.
|
||||
|
||||
---
|
||||
|
||||
## Priority 1 — Protect production before repair
|
||||
|
||||
- [ ] Take a database backup.
|
||||
- [ ] Export `invoices`, `payments`, `discount_usages`, `refunds`, `balance_transfers`, and any `payment_transactions` table.
|
||||
- [ ] Run all reconciliation SQL in read-only mode first.
|
||||
- [ ] Apply data repair only after confirming the result count of each query.
|
||||
|
||||
Recommended backup command:
|
||||
|
||||
```bash
|
||||
mysqldump -u <user> -p <database> invoices payments discount_usages refunds balance_transfers payment_transactions > payment_repair_backup.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 2 — Add missing schema support
|
||||
|
||||
The PHP controller writes `installment_seq`, but the uploaded `payments` schema only has `number_of_installments`.
|
||||
|
||||
Add a dedicated installment sequence column:
|
||||
|
||||
```sql
|
||||
ALTER TABLE payments
|
||||
ADD COLUMN installment_seq INT NULL AFTER number_of_installments;
|
||||
|
||||
UPDATE payments
|
||||
SET installment_seq = number_of_installments
|
||||
WHERE installment_seq IS NULL;
|
||||
```
|
||||
|
||||
Change `payment_date` from `DATE` to `DATETIME` so multiple same-day payments can be sorted correctly:
|
||||
|
||||
```sql
|
||||
ALTER TABLE payments
|
||||
MODIFY payment_date DATETIME NOT NULL;
|
||||
```
|
||||
|
||||
Add indexes:
|
||||
|
||||
```sql
|
||||
CREATE INDEX idx_payments_invoice_id ON payments(invoice_id);
|
||||
CREATE INDEX idx_payments_parent_year ON payments(parent_id, school_year, semester);
|
||||
CREATE UNIQUE INDEX uq_payments_transaction_id ON payments(transaction_id);
|
||||
CREATE INDEX idx_invoices_parent_year ON invoices(parent_id, school_year, semester);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 3 — Repair existing payment school year and semester
|
||||
|
||||
Payments should inherit their term from the linked invoice. The current data has payments marked `Spring` while their invoice is `Fall`.
|
||||
|
||||
Preview first:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
p.id AS payment_id,
|
||||
p.invoice_id,
|
||||
p.parent_id,
|
||||
p.school_year AS payment_school_year,
|
||||
p.semester AS payment_semester,
|
||||
i.school_year AS invoice_school_year,
|
||||
i.semester AS invoice_semester
|
||||
FROM payments p
|
||||
JOIN invoices i ON i.id = p.invoice_id
|
||||
WHERE p.school_year <> i.school_year
|
||||
OR p.semester <> i.semester
|
||||
ORDER BY p.id;
|
||||
```
|
||||
|
||||
Repair:
|
||||
|
||||
```sql
|
||||
UPDATE payments p
|
||||
JOIN invoices i ON i.id = p.invoice_id
|
||||
SET
|
||||
p.school_year = i.school_year,
|
||||
p.semester = i.semester
|
||||
WHERE
|
||||
p.school_year <> i.school_year
|
||||
OR p.semester <> i.semester;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 4 — Stop trusting stale copied fields
|
||||
|
||||
Do not use these fields as authoritative in the UI:
|
||||
|
||||
- `payments.total_amount`
|
||||
- `payments.balance`
|
||||
- `payments.status` as invoice status
|
||||
|
||||
Use:
|
||||
|
||||
- `payments.paid_amount` as the actual transaction amount.
|
||||
- `invoices.total_amount` as the invoice total.
|
||||
- `InvoiceLedgerService` for current invoice balance.
|
||||
- Invoice status from recalculation, not from the latest payment row.
|
||||
|
||||
The payment history table should display:
|
||||
|
||||
```text
|
||||
invoice_number
|
||||
payment_date
|
||||
paid_amount
|
||||
payment_method
|
||||
check_number
|
||||
transaction_id
|
||||
installment_seq
|
||||
balance_after_payment
|
||||
invoice_total
|
||||
invoice_current_balance
|
||||
invoice_status
|
||||
school_year
|
||||
semester
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 5 — Fix `manualPayUpdate()` term assignment
|
||||
|
||||
Current bug: the controller locks the invoice and reads the invoice school year, but then passes `$this->schoolYear` and `$this->semester` into `processPayment()`. That stores payment rows under the active config term instead of the invoice term.
|
||||
|
||||
Change the invoice lock query:
|
||||
|
||||
```php
|
||||
$row = $this->db->query(
|
||||
'SELECT id, parent_id, total_amount, school_year, semester FROM invoices WHERE id = ? FOR UPDATE',
|
||||
[$invoiceId]
|
||||
)->getRowArray();
|
||||
|
||||
$parentId = (int) ($row['parent_id'] ?? 0);
|
||||
$invYear = (string) ($row['school_year'] ?? $this->schoolYear);
|
||||
$invSemester = (string) ($row['semester'] ?? $this->semester);
|
||||
```
|
||||
|
||||
Then pass the invoice term into `processPayment()`:
|
||||
|
||||
```php
|
||||
$ok = $this->processPayment(
|
||||
$invoiceId,
|
||||
$amount,
|
||||
$paymentMethod,
|
||||
$checkFile,
|
||||
$transactionId,
|
||||
$paymentDate,
|
||||
$invYear,
|
||||
$invSemester,
|
||||
$checkNumber,
|
||||
$installmentSeq,
|
||||
(array) $this->invoiceModel->find($invoiceId),
|
||||
$currentBalance
|
||||
);
|
||||
```
|
||||
|
||||
Do not use the current configuration term when recording a payment for an existing invoice. The invoice owns the term.
|
||||
|
||||
---
|
||||
|
||||
## Priority 6 — Replace weak parent payment queries
|
||||
|
||||
Current parent payment lookup is too broad and does not include enough invoice context.
|
||||
|
||||
Replace simple payment-only queries with invoice-joined queries.
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
$selectedYear = $this->request->getGet('school_year') ?? $this->schoolYear;
|
||||
$selectedSemester = $this->request->getGet('semester') ?? null;
|
||||
|
||||
$paymentsQuery = $this->paymentModel
|
||||
->select([
|
||||
'payments.id',
|
||||
'payments.invoice_id',
|
||||
'invoices.invoice_number',
|
||||
'payments.transaction_id',
|
||||
'payments.paid_amount',
|
||||
'payments.payment_method',
|
||||
'payments.check_number',
|
||||
'payments.check_file',
|
||||
'payments.payment_date',
|
||||
'payments.installment_seq',
|
||||
'payments.number_of_installments',
|
||||
'payments.balance AS balance_after_payment',
|
||||
'payments.status AS payment_status',
|
||||
'invoices.total_amount AS invoice_total',
|
||||
'invoices.balance AS invoice_current_balance',
|
||||
'invoices.status AS invoice_status',
|
||||
'invoices.school_year',
|
||||
'invoices.semester',
|
||||
])
|
||||
->join('invoices', 'invoices.id = payments.invoice_id', 'inner')
|
||||
->where('payments.parent_id', $parentId)
|
||||
->where('invoices.school_year', $selectedYear);
|
||||
|
||||
if ($selectedSemester !== null && $selectedSemester !== '') {
|
||||
$paymentsQuery->where('invoices.semester', $selectedSemester);
|
||||
}
|
||||
|
||||
$payments = $paymentsQuery
|
||||
->orderBy('payments.payment_date', 'DESC')
|
||||
->orderBy('payments.id', 'DESC')
|
||||
->paginate(10);
|
||||
```
|
||||
|
||||
Apply this to:
|
||||
|
||||
- `getByParent()`
|
||||
- `manualPaySearch()`
|
||||
- Any dashboard or parent profile payment history endpoint
|
||||
- Any admin finance page reading payment history
|
||||
|
||||
---
|
||||
|
||||
## Priority 7 — Normalize payment status
|
||||
|
||||
Payment status and invoice status must not mean the same thing.
|
||||
|
||||
Payment statuses should be transaction-level:
|
||||
|
||||
```text
|
||||
Recorded
|
||||
Voided
|
||||
Refunded
|
||||
Failed
|
||||
Reversed
|
||||
Chargeback
|
||||
```
|
||||
|
||||
Invoice statuses should be computed:
|
||||
|
||||
```text
|
||||
Unpaid
|
||||
Partially Paid
|
||||
Paid
|
||||
Overpaid
|
||||
Cancelled
|
||||
```
|
||||
|
||||
After the UI stops depending on `payments.status` as invoice status, normalize existing successful payment rows:
|
||||
|
||||
```sql
|
||||
UPDATE payments
|
||||
SET status = 'Recorded'
|
||||
WHERE LOWER(status) IN ('paid', 'partially paid', 'payment recorded');
|
||||
```
|
||||
|
||||
Update `FinancialStatus` to include transaction statuses separately from invoice statuses.
|
||||
|
||||
---
|
||||
|
||||
## Priority 8 — Decide what to do with `payment_transactions`
|
||||
|
||||
`PaymentTransactionController` writes to a separate transaction model, but manual payments are recorded directly into `payments`.
|
||||
|
||||
Pick one architecture:
|
||||
|
||||
### Preferred
|
||||
|
||||
Use `payments` as the single payment transaction ledger.
|
||||
|
||||
Then:
|
||||
|
||||
- [ ] Retire payment transaction pages/routes if unused.
|
||||
- [ ] Do not build UI reports from `payment_transactions`.
|
||||
- [ ] Keep gateway attempts in a separate `payment_attempts` or `online_payment_transactions` table only if needed.
|
||||
|
||||
### Alternative
|
||||
|
||||
If `payment_transactions` must stay:
|
||||
|
||||
- [ ] Create a transaction row every time a payment row is inserted.
|
||||
- [ ] Do it inside the same DB transaction.
|
||||
- [ ] Never show `payment_transactions` without joining back to `payments`.
|
||||
|
||||
Preferred option is cleaner. Two ledgers become two lies unless aggressively synchronized.
|
||||
|
||||
---
|
||||
|
||||
## Priority 9 — Recalculate invoice ledger
|
||||
|
||||
After schema and controller fixes, recalculate every invoice through `InvoiceLedgerService`.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- [ ] Sum successful payments from `payments`.
|
||||
- [ ] Subtract discounts from `discount_usages`.
|
||||
- [ ] Apply refunds/credits consistently.
|
||||
- [ ] Include additional/event charges if they belong in invoice total.
|
||||
- [ ] Update cached `invoices.paid_amount`, `invoices.balance`, and `invoices.status`.
|
||||
- [ ] Never calculate invoice balance directly inside controllers.
|
||||
|
||||
Suggested maintenance command:
|
||||
|
||||
```php
|
||||
foreach ($invoiceModel->findAll() as $invoice) {
|
||||
$invoiceLedgerService->recalculateInvoice((int) $invoice['id']);
|
||||
}
|
||||
```
|
||||
|
||||
Run this from a CLI command, not from a browser request.
|
||||
|
||||
---
|
||||
|
||||
## Priority 10 — Reconciliation SQL
|
||||
|
||||
Run these checks after repair.
|
||||
|
||||
### Stale payment totals
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
p.id AS payment_id,
|
||||
p.invoice_id,
|
||||
p.parent_id,
|
||||
p.total_amount AS payment_total_amount,
|
||||
i.total_amount AS invoice_total_amount,
|
||||
p.paid_amount,
|
||||
p.balance,
|
||||
p.payment_date
|
||||
FROM payments p
|
||||
JOIN invoices i ON i.id = p.invoice_id
|
||||
WHERE p.total_amount <> i.total_amount
|
||||
ORDER BY p.invoice_id, p.id;
|
||||
```
|
||||
|
||||
Expected after UI fix: rows may still exist historically, but the UI must not rely on `payments.total_amount`.
|
||||
|
||||
### Term mismatch
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
p.id AS payment_id,
|
||||
p.invoice_id,
|
||||
p.parent_id,
|
||||
p.school_year AS payment_school_year,
|
||||
p.semester AS payment_semester,
|
||||
i.school_year AS invoice_school_year,
|
||||
i.semester AS invoice_semester
|
||||
FROM payments p
|
||||
JOIN invoices i ON i.id = p.invoice_id
|
||||
WHERE p.school_year <> i.school_year
|
||||
OR p.semester <> i.semester
|
||||
ORDER BY p.id;
|
||||
```
|
||||
|
||||
Expected: zero rows.
|
||||
|
||||
### Negative payment balance snapshots
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id,
|
||||
parent_id,
|
||||
invoice_id,
|
||||
total_amount,
|
||||
paid_amount,
|
||||
balance,
|
||||
status,
|
||||
payment_date
|
||||
FROM payments
|
||||
WHERE balance < 0
|
||||
ORDER BY invoice_id, id;
|
||||
```
|
||||
|
||||
Expected: zero rows after balance snapshot repair, or ignored if `payments.balance` is deprecated.
|
||||
|
||||
### Invoices with no payment rows
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
i.id,
|
||||
i.parent_id,
|
||||
i.invoice_number,
|
||||
i.total_amount,
|
||||
i.paid_amount,
|
||||
i.balance,
|
||||
i.has_discount,
|
||||
i.status
|
||||
FROM invoices i
|
||||
LEFT JOIN payments p ON p.invoice_id = i.id
|
||||
WHERE p.id IS NULL
|
||||
ORDER BY i.id;
|
||||
```
|
||||
|
||||
Expected: only invoices fully covered by discounts, credits, cancellations, or legitimate zero-payment states.
|
||||
|
||||
### Invoice paid amount consistency
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
i.id AS invoice_id,
|
||||
i.parent_id,
|
||||
i.invoice_number,
|
||||
i.paid_amount AS invoice_paid_amount,
|
||||
COALESCE(SUM(p.paid_amount), 0) AS payment_sum
|
||||
FROM invoices i
|
||||
LEFT JOIN payments p ON p.invoice_id = i.id
|
||||
GROUP BY i.id, i.parent_id, i.invoice_number, i.paid_amount
|
||||
HAVING ROUND(i.paid_amount, 2) <> ROUND(payment_sum, 2)
|
||||
ORDER BY i.id;
|
||||
```
|
||||
|
||||
Expected: zero rows, unless invoice paid amount intentionally includes imported legacy payments that are not in `payments`.
|
||||
|
||||
---
|
||||
|
||||
## Priority 11 — Tests to add
|
||||
|
||||
### Unit tests
|
||||
|
||||
- [ ] Payment creation uses invoice `school_year` and `semester`, not config term.
|
||||
- [ ] Payment amount cannot exceed current ledger balance.
|
||||
- [ ] Card payment must equal full current balance.
|
||||
- [ ] Editing a payment recalculates invoice balance.
|
||||
- [ ] `installment_seq` increments only successful payments.
|
||||
- [ ] Voided/refunded/failed payments are excluded from balance.
|
||||
- [ ] `payments.total_amount` is never used as current invoice total in DTOs.
|
||||
|
||||
### Integration tests
|
||||
|
||||
- [ ] Parent payment history returns payments joined to invoice number.
|
||||
- [ ] Parent payment history filters by invoice school year.
|
||||
- [ ] Parent payment history filters by invoice semester.
|
||||
- [ ] Admin finance table shows transaction amount and current invoice balance correctly.
|
||||
- [ ] Manual payment insert, invoice recalculation, and notification event are atomic.
|
||||
|
||||
### Data repair tests
|
||||
|
||||
- [ ] Term mismatch query returns zero rows after repair.
|
||||
- [ ] Negative payment balance query returns zero rows or deprecated field is hidden.
|
||||
- [ ] Invoice paid amount consistency query returns zero rows.
|
||||
- [ ] Invoices with no payments are manually classified as discount-only, cancelled, imported, or broken.
|
||||
|
||||
---
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
The fix is complete only when:
|
||||
|
||||
- [ ] Existing payments are visible in parent and admin payment history.
|
||||
- [ ] Payment history shows transaction amount, not stale invoice total.
|
||||
- [ ] Payment rows inherit school year and semester from invoice.
|
||||
- [ ] No payment row has mismatched invoice term.
|
||||
- [ ] No new payment insert depends on global config term for existing invoices.
|
||||
- [ ] `installment_seq` exists and is populated.
|
||||
- [ ] Invoice balances are recalculated by `InvoiceLedgerService`.
|
||||
- [ ] `payment_transactions` is either retired or explicitly separated as gateway-attempt history.
|
||||
- [ ] Reconciliation SQL has been run and saved as evidence.
|
||||
|
||||
---
|
||||
|
||||
## Do not do this
|
||||
|
||||
- Do not manually edit invoice balances row by row.
|
||||
- Do not use `payments.total_amount` as invoice total.
|
||||
- Do not use `payments.status` as invoice status.
|
||||
- Do not filter payment history only by `payments.school_year` if invoice term exists.
|
||||
- Do not keep two independent payment ledgers unless one is clearly marked as gateway attempts.
|
||||
- Do not trust the latest payment row balance as the current invoice balance.
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
* Sample file for Preloading
|
||||
*---------------------------------------------------------------
|
||||
* See https://www.php.net/manual/en/opcache.preloading.php
|
||||
*
|
||||
* How to Use:
|
||||
* 0. Copy this file to your project root folder.
|
||||
* 1. Set the $paths property of the preload class below.
|
||||
* 2. Set opcache.preload in php.ini.
|
||||
* php.ini:
|
||||
* opcache.preload=/path/to/preload.php
|
||||
*/
|
||||
|
||||
// Load the paths config file
|
||||
require __DIR__ . '/app/Config/Paths.php';
|
||||
|
||||
// Path to the front controller
|
||||
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR);
|
||||
|
||||
class preload
|
||||
{
|
||||
/**
|
||||
* @var array Paths to preload.
|
||||
*/
|
||||
private array $paths = [
|
||||
[
|
||||
'include' => __DIR__ . '/vendor/codeigniter4/framework/system', // Change this path if using manual installation
|
||||
'exclude' => [
|
||||
'/system/bootstrap.php',
|
||||
// Not needed if you don't use them.
|
||||
'/system/Database/OCI8/',
|
||||
'/system/Database/Postgre/',
|
||||
'/system/Database/SQLite3/',
|
||||
'/system/Database/SQLSRV/',
|
||||
// Not needed.
|
||||
'/system/Database/Seeder.php',
|
||||
'/system/Test/',
|
||||
'/system/Language/',
|
||||
'/system/CLI/',
|
||||
'/system/Commands/',
|
||||
'/system/Publisher/',
|
||||
'/system/ComposerScripts.php',
|
||||
'/Views/',
|
||||
// Errors occur.
|
||||
'/system/Config/Routes.php',
|
||||
'/system/ThirdParty/',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->loadAutoloader();
|
||||
}
|
||||
|
||||
private function loadAutoloader(): void
|
||||
{
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'Boot.php';
|
||||
|
||||
CodeIgniter\Boot::preload($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load PHP files.
|
||||
*/
|
||||
public function load(): void
|
||||
{
|
||||
foreach ($this->paths as $path) {
|
||||
$directory = new RecursiveDirectoryIterator($path['include']);
|
||||
$fullTree = new RecursiveIteratorIterator($directory);
|
||||
$phpFiles = new RegexIterator(
|
||||
$fullTree,
|
||||
'/.+((?<!Test)+\.php$)/i',
|
||||
RecursiveRegexIterator::GET_MATCH
|
||||
);
|
||||
|
||||
foreach ($phpFiles as $key => $file) {
|
||||
foreach ($path['exclude'] as $exclude) {
|
||||
if (str_contains($file[0], $exclude)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
require_once $file[0];
|
||||
echo 'Loaded: ' . $file[0] . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(new preload())->load();
|
||||
@@ -0,0 +1,75 @@
|
||||
//create the docker image:
|
||||
docker build -t lamp-codeigniter4 .
|
||||
docker build --no-cache -t lamp-codeigniter4 .
|
||||
|
||||
//run the container only port 80 exposed.
|
||||
docker run -d -p 8080:80 --name lamp-codeigniter4-container lamp-codeigniter4
|
||||
|
||||
//run the container with port 80, 3306 exposed.
|
||||
docker run -d -p 8080:80 -p 3306:3306 --name lamp-codeigniter4-container lamp-codeigniter4
|
||||
|
||||
|
||||
|
||||
//run the shell on container:
|
||||
docker exec -it lamp-codeigniter4-container bash
|
||||
|
||||
//install nano
|
||||
apt install nano
|
||||
|
||||
|
||||
//open mysql and execute the following
|
||||
mysql -u root -ppassword
|
||||
|
||||
//create user and grant access to localhost
|
||||
CREATE USER 'admin'@'localhost' IDENTIFIED BY 'password';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'alrahma'@'localhost' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
|
||||
//create user and grant access to "192.168.3.%"
|
||||
CREATE USER 'alrahma'@'192.168.3.%' IDENTIFIED BY '@alrahma2024';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'alrahma'@'192.168.3.%' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
//exit mysql.
|
||||
|
||||
//update the config to enable accepting requests from all IP's
|
||||
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf //and add thefollowing line
|
||||
bind-address = 0.0.0.0
|
||||
|
||||
sudo service mysql restart
|
||||
|
||||
|
||||
//update these files database config, open each file
|
||||
nano /var/www/html/codeigniter4/app/Views/frontend/partials/navbar.php
|
||||
nano /var/www/html/codeigniter4/app/db_connection.php
|
||||
nano /var/www/codeigniter/app/Config/Database.php
|
||||
nano /var/www/codeigniter/app/Controllers/ParentController.php
|
||||
|
||||
and look for those lines
|
||||
$host = 'localhost';
|
||||
$dbname = 'school';
|
||||
$username = 'root';
|
||||
$password = '';
|
||||
|
||||
|
||||
|
||||
and update them as follow:
|
||||
|
||||
// new Database configuration
|
||||
$host = '127.0.0.1';
|
||||
$dbname = 'school';
|
||||
$username = 'admin';
|
||||
$password = 'password';
|
||||
|
||||
// Database configuration
|
||||
$host = '192.168.3.110';
|
||||
$dbname = 'school';
|
||||
$username = 'alrahma';
|
||||
$password = '@alrahma2024';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
apt update
|
||||
apt upgrade
|
||||
|
||||
nano /var/ww/codeigniter/app/Config/App.php
|
||||
|
||||
#copy the content of the project folder into "/var/www/codeigniter/"
|
||||
|
||||
#make sure the permissions is granted
|
||||
chown -R www-data:www-data /var/www/codeigniter/writable/cache/
|
||||
chmod -R 775 /var/www/codeigniter/writable/cache/
|
||||
chown -R www-data:www-data /var/www/codeigniter/writable/session/
|
||||
chmod -R 775 /var/www/codeigniter/writable/session/
|
||||
|
||||
rm -rf /var/www/codeigniter/writable/cache/*
|
||||
systemctl restart apache2
|
||||
|
||||
nano /etc/mysql/mariadb.conf.d/50-server.cnf or nano /etc/mysql/my.cnf
|
||||
bind-address = 0.0.0.0
|
||||
|
||||
|
||||
#run myphpadmin in docker container poiting to remote database
|
||||
docker run --name myphpmyadmin -d \
|
||||
-e PMA_HOST=192.168.3.126 \
|
||||
-e PMA_PORT=3306 \
|
||||
-e PMA_USER=alrahma \
|
||||
-e PMA_PASSWORD=@alrahma2024 \
|
||||
-p 8080:80 \
|
||||
phpmyadmin
|
||||
@@ -0,0 +1,150 @@
|
||||
ALTER TABLE `student_class`
|
||||
ADD UNIQUE KEY `uq_student_semester_year` (`student_id`, `semester`, `school_year`);
|
||||
|
||||
ALTER TABLE `enrollments`
|
||||
ADD INDEX `idx_enrollment_lookup` (`student_id`, `semester`, `school_year`);
|
||||
|
||||
|
||||
-- 1) Add generated normalized columns (stored so they can be indexed)
|
||||
ALTER TABLE students
|
||||
ADD COLUMN firstname_norm VARCHAR(100)
|
||||
GENERATED ALWAYS AS (LOWER(TRIM(firstname))) STORED,
|
||||
ADD COLUMN lastname_norm VARCHAR(100)
|
||||
GENERATED ALWAYS AS (LOWER(TRIM(lastname))) STORED;
|
||||
|
||||
-- 2) Prevent duplicates for the same student, same DOB & school year
|
||||
-- 2) (Optional but recommended) If you have a multi-school setup, include school identifier
|
||||
-- Otherwise, skip school_id in the index.
|
||||
CREATE UNIQUE INDEX ux_students_unique_per_year_global
|
||||
ON students (school_year, dob, firstname_norm, lastname_norm);
|
||||
-- Or, if you have multiple schools:
|
||||
-- CREATE UNIQUE INDEX ux_students_unique_per_year_global
|
||||
-- ON students (school_id, school_year, dob, firstname_norm, lastname_norm);
|
||||
|
||||
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS email_templates (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
template_key VARCHAR(64) NOT NULL UNIQUE, -- 'attendance_notice', 'grade_update', 'behavior_note'
|
||||
name VARCHAR(100) NOT NULL,
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
body MEDIUMTEXT NOT NULL,
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS families (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
family_code VARCHAR(32) UNIQUE,
|
||||
household_name VARCHAR(120) NULL,
|
||||
address_line1 VARCHAR(150) NULL,
|
||||
address_line2 VARCHAR(150) NULL,
|
||||
city VARCHAR(80) NULL,
|
||||
state VARCHAR(40) NULL,
|
||||
postal_code VARCHAR(20) NULL,
|
||||
country VARCHAR(2) NULL,
|
||||
primary_phone VARCHAR(40) NULL,
|
||||
preferred_lang VARCHAR(10) DEFAULT 'en',
|
||||
preferred_contact_method ENUM('email','sms','phone') DEFAULT 'email',
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS family_students (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
family_id INT UNSIGNED NOT NULL,
|
||||
student_id INT UNSIGNED NOT NULL,
|
||||
is_primary_home TINYINT(1) DEFAULT 1,
|
||||
notes VARCHAR(255) NULL,
|
||||
UNIQUE KEY uq_family_student (family_id, student_id),
|
||||
KEY idx_fs_student (student_id),
|
||||
CONSTRAINT fk_fs_family FOREIGN KEY (family_id) REFERENCES families(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_fs_student FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS family_comm_prefs (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
family_id INT UNSIGNED NOT NULL,
|
||||
category ENUM('attendance','grade','behavior','general') NOT NULL,
|
||||
via_email TINYINT(1) DEFAULT 1,
|
||||
via_sms TINYINT(1) DEFAULT 0,
|
||||
cc_all_guardians TINYINT(1) DEFAULT 1,
|
||||
UNIQUE KEY uq_family_category (family_id, category),
|
||||
CONSTRAINT fk_fcp_family FOREIGN KEY (family_id) REFERENCES families(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS communication_logs (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
student_id INT UNSIGNED NOT NULL,
|
||||
family_id INT UNSIGNED NULL,
|
||||
student_name VARCHAR(150) NOT NULL,
|
||||
template_key VARCHAR(64) NOT NULL,
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
body MEDIUMTEXT NOT NULL,
|
||||
recipients TEXT NOT NULL,
|
||||
cc TEXT NULL,
|
||||
bcc TEXT NULL,
|
||||
attachments TEXT NULL,
|
||||
status ENUM('sent','failed') NOT NULL,
|
||||
error_message TEXT NULL,
|
||||
sent_by INT UNSIGNED NULL,
|
||||
metadata JSON NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_comm_student (student_id),
|
||||
KEY idx_comm_family (family_id),
|
||||
KEY idx_comm_template (template_key),
|
||||
CONSTRAINT fk_comm_family
|
||||
FOREIGN KEY (family_id) REFERENCES families(id)
|
||||
ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
|
||||
|
||||
INSERT IGNORE INTO email_templates (template_key, name, subject, body) VALUES
|
||||
('attendance_notice', 'Attendance Notice',
|
||||
'Attendance Update for {{student_fullname}}',
|
||||
'Dear {{parent_salutation}},\n\nThis is an update regarding {{student_fullname}} ({{student_grade}}). {{attendance_message}}\n\nDate: {{date}}\nClass/Section: {{class_section}}\n\nIf you have any questions, please reply to this email.\n\nRegards,\n{{school_name}}'),
|
||||
('grade_update', 'Grade Update',
|
||||
'Grade Update for {{student_fullname}}',
|
||||
'Dear {{parent_salutation}},\n\nWe would like to share a grade update for {{student_fullname}} ({{student_grade}}).\n\nAssessment: {{assessment_name}}\nScore: {{score_obtained}} / {{score_total}}\nComments: {{teacher_comments}}\n\nPlease reach out if you need more details.\n\nRegards,\n{{teacher_name}} — {{school_name}}'),
|
||||
('behavior_note', 'Behavior Note',
|
||||
'Behavior Note for {{student_fullname}}',
|
||||
'Dear {{parent_salutation}},\n\nWe want to inform you about {{student_fullname}}\'s behavior on {{date}}.\n\nSummary: {{behavior_summary}}\nActions Taken: {{actions_taken}}\nNext Steps: {{next_steps}}\n\nThank you for your partnership.\n\nRegards,\n{{teacher_name}} — {{school_name}}');
|
||||
|
||||
|
||||
|
||||
|
||||
CREATE TABLE `family_guardians` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`family_id` INT UNSIGNED NOT NULL,
|
||||
`user_id` INT UNSIGNED NOT NULL,
|
||||
`relation` VARCHAR(32) DEFAULT NULL,
|
||||
`is_primary` TINYINT(1) DEFAULT 0,
|
||||
`receive_emails` TINYINT(1) DEFAULT 1,
|
||||
`receive_sms` TINYINT(1) DEFAULT 0,
|
||||
`custody_notes` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_family_user` (`family_id`,`user_id`),
|
||||
KEY `idx_fg_family` (`family_id`),
|
||||
KEY `idx_fg_user` (`user_id`),
|
||||
CONSTRAINT `fk_fg_family`
|
||||
FOREIGN KEY (`family_id`) REFERENCES `school`.`families` (`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_family_guardians_user_id`
|
||||
FOREIGN KEY (`user_id`) REFERENCES `school`.`users` (`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
ALTER TABLE roles
|
||||
ADD COLUMN slug VARCHAR(64) DEFAULT NULL AFTER name,
|
||||
ADD COLUMN dashboard_route VARCHAR(255) NOT NULL DEFAULT '/landing_page/guest_dashboard' AFTER description,
|
||||
ADD COLUMN priority INT(10) UNSIGNED NOT NULL DEFAULT 100 AFTER dashboard_route,
|
||||
ADD COLUMN is_active TINYINT(1) NOT NULL DEFAULT 1 AFTER priority;
|
||||
Reference in New Issue
Block a user