nhance/app/Controllers/AppContentManagementController.php

614 lines
27 KiB
PHP
Executable File

<?php
// declare(strict_types=1);
namespace App\Controllers;
use Psr\Log\LoggerInterface;
use CodeIgniter\API\ResponseTrait;
use App\Models\AddImgModel;
use App\Models\FEContentModel;
use App\Models\ClientModel;
use App\Models\FAQModel;
class AppContentManagementController extends AdminController
{
use ResponseTrait;
protected $myLogger;
protected $addImgModel;
protected $feContentModel;
protected $clientModel;
protected $faqModel;
public function __construct()
{
$this->myLogger = \Config\Services::mylogger();
$this->addImgModel = new AddImgModel();
$this->feContentModel = new FEContentModel();
$this->clientModel = new ClientModel();
$this->faqModel = new FAQModel();
}
//listing
public function add_image_index()
{
$headerData['tab_name'] = 'Advertisement Images';
$headerData['page_name'] = 'Advertisement Images';
// $data['addImageList'] = $this->addImgModel->findAll();
$data['addImageList'] = $this->addImgModel->select('advertisement_images.*,
clients.client_name,
clients.short_name,
CASE WHEN advertisement_images.is_active = 1 THEN "Active" ELSE "Inactive" END AS status', false)
->join('clients', 'advertisement_images.client_id = clients.id', 'left')
->where('advertisement_images.is_active', 1)
->findAll();
$data['client'] = $this->clientModel->where('is_active', 1)->where('client_type', 1)->findAll();
// dd($data);
echo view('layout/header', $headerData);
echo view('add_image_list', $data);
echo view('layout/footer');
// $this->loadLayout('client_onboarding', $data);
}
// add and edit
public function add_advertise_image() {
try {
$data = $this->request->getPost();
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
$id = (int)($sanitized_post_data['add_image_id'] ?? 0);
$rules = [
'add_image_id' => [
'rules' => 'permit_empty|integer|is_natural',
'errors' => [
'integer' => 'Image ID must be a valid number',
'is_natural' => 'Image ID must be a non-negative number'
]
],
'client_id' => [
'rules' => 'required|integer|is_natural_no_zero',
'errors' => [
'required' => 'Client is required',
'integer' => 'Invalid client selected',
'is_natural_no_zero' => 'Invalid client selected'
]
],
'advertise_image' => [
'rules' => ($id === 0 ? 'uploaded[advertise_image]|' : '')
. 'is_image[advertise_image]'
. '|mime_in[advertise_image,image/jpg,image/jpeg,image/png]'
. '|max_size[advertise_image,200]'
. '|min_dims[advertise_image,1640,664]'
. '|max_dims[advertise_image,1640,664]',
'errors' => [
'uploaded' => 'Image is required',
'is_image' => 'File must be an image',
'mime_in' => 'Only JPG, JPEG, PNG allowed',
'max_size' => 'Image size must not exceed 200 KB',
'min_dims' => 'Image dimensions must be exactly 1640x664 pixels',
'max_dims' => 'Image dimensions must be exactly 1640x664 pixels',
]
],
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false,
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors()
]);
}
$file = $this->request->getFile('advertise_image');
$client_id = (int)($sanitized_post_data['client_id'] ?? 0);
$clientExists = $this->clientModel->where('id', $client_id)->where('is_active', 1)->first();
if (!$clientExists) {
return $this->respond(['status' => false, 'message' => 'Invalid client selected.'], 400);
}
if (!$file || !$file->isValid()) {
return $this->respond(['status' => false, 'message' => 'No file uploaded or invalid file.'], 400);
}
if (!validate_upload_extension($file, UPLOAD_EXT_IMAGES)) {
return $this->respond(['status' => false, 'message' => 'Only JPG, JPEG, PNG files are allowed.'], 400);
}
$fileName = sanitize_upload_filename($file->getClientName());
$existing = $this->addImgModel->where('name', $fileName)->where('client_id', $client_id)->where('is_active', 1)->first();
if($existing){ return $this->respond(['status' => false, 'message' => 'This file has already been uploaded in active state.'], 400); }
$uploadPath = ROOTPATH . 'public/uploads/add_image_upload/';
if (!is_dir($uploadPath)) mkdir($uploadPath, 0755, true);
$file->move($uploadPath, $fileName);
$details = ['name' => $fileName,'client_id'=>$client_id];
if ($id === 0) {
$this->addImgModel->insert($details);
} else {
$existingRecord = $this->addImgModel->find($id);
if (!$existingRecord) {
return $this->respond(['status' => false, 'message' => 'Record not found for update.'], 404);
}
$this->addImgModel->update($id, $details);
}
return $this->respond(['status' => true, 'message' => 'Image saved successfully.']);
} catch (\Exception $e) {
return $this->respond(['status' => false, 'message' => $e->getMessage()], 500);
}
}
// soft delete
public function remove_advertise_image()
{
try {
$rules = [
'add_image_id' => [
'rules' => 'required|integer|is_natural_no_zero',
'errors' => [
'required' => 'Image ID is required',
'integer' => 'Image ID must be a valid number',
'is_natural_no_zero' => 'Image ID must be a positive number'
]
]
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false,
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors()
]);
}
$id = (int)$this->request->getPost('add_image_id');
$existing = $this->addImgModel->find($id);
if (!$existing) {
return $this->respond(['status' => false, 'message' => 'Record not found'], 404);
}
$this->addImgModel->update($id, ['is_active' => 0]);
return $this->respond(['status' => true, 'message' => 'Deleted successfully']);
} catch (\Exception $e) {
return $this->respond(['status' => false, 'message' => $e->getMessage()], 500);
}
}
// Preview image Went Edit.
public function showAdvertiseImage($filename)
{
$filename = basename($filename);
if (!preg_match('/^[a-zA-Z0-9_\-]+\.(jpg|jpeg|png|gif|webp)$/i', $filename)) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$path = ROOTPATH . 'public/uploads/add_image_upload/' . $filename;
$realPath = realpath($path);
$allowedDir = realpath(ROOTPATH . 'public/uploads/add_image_upload');
if (!$realPath || strpos($realPath, $allowedDir) !== 0 || !file_exists($realPath)) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$mime = mime_content_type($realPath);
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($mime, $allowedMimes, true)) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
return $this->response->setHeader('Content-Type', $mime)->setBody(file_get_contents($realPath));
}
public function frontend_content()
{
$method = $this->request->getMethod();
if ($this->request->getMethod() === 'post') {
$rules = [
'fe_id' => [
'rules' => 'permit_empty|integer|is_natural',
'errors' => [
'integer' => 'ID must be a valid number',
'is_natural' => 'ID must be a non-negative number'
]
],
'type' => [
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]',
'errors' => [
'required' => 'Type is required',
'max_length' => 'Type cannot exceed 255 characters',
'regex_match' => 'Type contains invalid characters'
]
],
'content_section' => [
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]',
'errors' => [
'required' => 'Content Section is required',
'max_length' => 'Content Section cannot exceed 255 characters',
'regex_match' => 'Content Section contains invalid characters'
]
],
'heading' => [
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/]+$/]',
'errors' => [
'required' => 'Heading is required',
'max_length' => 'Heading cannot exceed 255 characters',
'regex_match' => 'Heading contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
]
],
'content' => [
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
'errors' => [
'required' => 'Content is required',
'max_length' => 'Content cannot exceed 5000 characters',
'regex_match' => 'Content contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
]
],
'notes' => [
'rules' => 'required|max_length[1500]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
'errors' => [
'required' => 'Notes are required',
'max_length' => 'Notes cannot exceed 1500 characters',
'regex_match' => 'Notes contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
]
]
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false,
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors()
]);
}
$request_post_data = $this->request->getPost();
$data = sanitizeInputArrayAdvanced($request_post_data);
$id = $data['fe_id'] ?? null;
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false, 'message' => 'Invalid ID format', 'code' => 400
]);
}
$id = !empty($id) ? (int)$id : null;
unset($data['fe_id']);
foreach ($data as $k => $v) {
if ($v === '' || $v === null) {unset($data[$k]);}
}
// INSERT / UPDATE
if (empty($id)) {
$status = $this->feContentModel->insert($data);
$text = "Created";
} else {
$existing = $this->feContentModel->find($id);
if (!$existing) {
return $this->response->setStatusCode(404)->setJSON([
'status' => false, 'message' => 'Record not found', 'code' => 404
]);
}
$status = $this->feContentModel->update($id, $data);
$text = "Updated";
}
return $this->respond([
'status' => $status ? true : false,
'message' => "Frontend Content $text " . ($status ? 'successfully' : 'failed'),
'code' => $status ? 200 : 400,
'data' => $data,
] , $status ? 200 : 400 );
}
elseif ($method === 'get') {
$id = $this->request->getGet('fe_id') ?? null;
if (!empty($id)) {
if (!ctype_digit((string)$id) || (int)$id <= 0) {
return $this->response->setStatusCode(400)->setJSON([
'status' => false, 'code' => 400, 'message' => 'Invalid ID format'
]);
}
$id = (int)$id;
$data['fe_list'] = $this->feContentModel->where('id', $id)->orderBy('id', 'DESC')->findAll();
if (!empty($data)) {
return $this->respond(['status' => true, 'code' => 200, 'data' => $data], 200);
} else {
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No data found'], 200);
}
}
$data['fe_list'] = $this->feContentModel->orderBy('id', 'DESC')->findAll();
return $this->loadLayout('frontend_content_list', ['data' => $data,'tab_name' => 'Front-End Content','page_name' => 'Front-End Content']);
} elseif ($method === 'delete') {
$id = $this->request->getGet('fe_id') ?? null;
if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'Valid numeric ID is required for deletion'
], 400);
}
$id = (int)$id;
$existing = $this->feContentModel->find($id);
if (!$existing) {
return $this->respond([
'status' => false,
'code' => 404,
'message' => 'Record not found'
], 404);
}
$update_status = $this->feContentModel->where('id', $id)->set(['is_active' => 0])->update();
if ($update_status) {
return $this->respond([
'status' => true,
'code' => 200,
'message' => 'Data removed successfully',
'fe_id' => $id
], 200);
} else {
return $this->respond([
'status' => false,
'code' => 400,
'message' => 'Failed to remove data',
'fe_id' => $id
], 200);
}
}
}
private const ALLOWED_RETURN_TYPES = ['api', 'web'];
public function FAQ()
{
$method = strtolower($this->request->getMethod());
$returnType = strtolower($this->request->getGet('return_type') ?? 'api');
if (!in_array($returnType, self::ALLOWED_RETURN_TYPES, true)) {
$returnType = 'api';
}
$ref = ['timestamp' => date('Y-m-d H:i:s')];
try {
// --- 1. POST: CREATE OR UPDATE ---
if ($method === 'post') {
$rules = [
'category' => [
'rules' => 'required|max_length[100]|alpha_numeric_space',
'errors' => [
'required' => 'Category is required',
'max_length' => 'Category cannot exceed 100 characters',
'alpha_numeric_space' => 'Category contains invalid characters'
]
],
'question' => [
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
'errors' => [
'required' => 'Question is required',
'max_length' => 'Question cannot exceed 1000 characters',
'regex_match' => 'Question contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
]
],
'answer' => [
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
'errors' => [
'required' => 'Answer is required',
'max_length' => 'Answer cannot exceed 5000 characters',
'regex_match' => 'Answer contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
]
]
];
if (!$this->validate($rules)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => 'error',
'message' => 'Input validation failed',
'code' => 400,
'errors' => $this->validator->getErrors(),
'ref' => $ref
]);
}
$request_post_data = $this->request->getPost();
$sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
$data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null);
$id = $data['faq_id'] ?? null;
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
return $this->response->setStatusCode(400)->setJSON([
'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref
]);
}
$id = !empty($id) ? (int)$id : null;
unset($data['faq_id']);
if (empty($id)) {
$status = $this->faqModel->insert($data);
$msg = "Created";
} else {
$existing = $this->faqModel->find($id);
if (!$existing) {
return $this->response->setStatusCode(404)->setJSON([
'status' => 'error', 'message' => 'FAQ not found', 'code' => 404, 'ref' => $ref
]);
}
$status = $this->faqModel->update($id, $data);
$msg = "Updated";
}
// if ($returnType === 'web') {
// return redirect()->back()->with($status ? 'success' : 'error', "FAQ $msg " . ($status ? 'successfully' : 'failed'));
// }
// return $this->response->setJSON([
// ])->setStatusCode($result ? 200 : 400);
return $this->response->setJSON([
'status' => $status ? 'success' : 'error',
'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'),
'code' => $status ? 200 : 400,
'data' => $data,
'ref' => $ref
])->setStatusCode($status ? 200 : 400);
}
// --- 2. GET: FETCH LIST OR SINGLE ---
elseif ($method === 'get') {
$id = $this->request->getGet('faq_id');
if (!empty($id)) {
if (!ctype_digit((string)$id) || (int)$id <= 0) {
return $this->response->setStatusCode(400)->setJSON([
'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref
]);
}
$id = (int)$id;
if($returnType === 'web'){
$row = $this->faqModel->find($id);
}else{
$row = $this->faqModel->where('is_active', 1)->find($id);
}
$data['faq_list'] = $row ? [$row] : [];
} else {
if($returnType === 'web'){
$data['faq_list'] = $this->faqModel->orderBy('id', 'desc')->findAll();
}else{
$data['faq_list'] = $this->faqModel->where('is_active', 1)->orderBy('id', 'desc')->findAll();
}
}
if ($returnType === 'web') {
$data['tab_name'] = "FAQ";
$data['page_name'] = "FAQ";
// print_r($data);die;
return $this->loadLayout('faq_list', $data);
}
// API Response Logic
if (empty($data['faq_list'])) {
return $this->response->setJSON([
'status' => $returnType === 'web' ? false : 'error',
'message' => 'No data found',
'code' => 404,
'data' => [],
'ref' => $ref
])->setStatusCode(200); // Using 200 with error status is common for mobile apps to prevent crashes
}
return $this->response->setJSON([
'status' => $returnType === 'web' ? true : 'success',
'message' => 'Data retrieved',
'code' => 200,
'data' => $data,
'ref' => $ref
])->setStatusCode(200);
}
// --- 3. DELETE: SOFT DELETE ---
elseif ($method === 'delete') {
$id = $this->request->getGet('faq_id');
if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) {
return $this->response->setJSON([
'status' => 'error', 'message' => 'Valid numeric FAQ ID is required', 'code' => 400, 'ref' => $ref
])->setStatusCode(400);
}
$id = (int)$id;
$status = $this->faqModel->find($id) ? $this->faqModel->update($id, ['is_active' => 0]) : false;
return $this->response->setJSON([
'status' => $status ? ($returnType === 'web' ? true : 'success') : ($returnType === 'web' ? false : 'error'),
'message' => $status ? 'Data removed successfully' : 'Failed to remove or ID missing',
'code' => $status ? 200 : 400,
'data' => [],
'ref' => $ref
])->setStatusCode( 200 );
}
} catch (\Throwable $e) {
log_message('error', 'FAQ error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
return $this->response->setJSON([
'status' => $returnType === 'web' ? false : 'error',
'code' => 500,
'message' => 'An internal error occurred. Please try again later.',
'ref' => $ref
])->setStatusCode(500);
}
}
// public function frontend_content_files()
// {
// // 1. Define your physical system path
// define('UPLOAD_PATH', ROOTPATH . 'public/uploads/add_image_upload/');
// // 2. Define the public URL so the editor can display the image later
// // This is what the browser uses to see the image (e.g., https://site.com/uploads/...)
// // $publicUrl = "https://yourdomain.com/public/uploads/add_image_upload/";
// $publicUrl = ROOTPATH . 'public/uploads/add_image_upload/';
// // if (!is_dir($uploadPath)) mkdir($uploadPath, 0755, true);
// header('Content-Type: application/json');
// if ($_FILES['files']) {
// // Ensure directory exists
// if (!is_dir(UPLOAD_PATH)) {
// mkdir(UPLOAD_PATH, 0775, true);
// }
// $fileName = time() . '_' . basename($_FILES['files']['name'][0]);
// $fullPath = UPLOAD_PATH . $fileName;
// if (move_uploaded_file($_FILES['files']['tmp_name'][0], $fullPath)) {
// echo json_encode([
// "success" => true,
// "data" => [
// "baseurl" => $publicUrl,
// "files" => [$fileName]
// ]
// ]);
// } else {
// echo json_encode(["success" => false, "error" => "Failed to move file to " . UPLOAD_PATH]);
// }
// }
// }
}