Initial commit

This commit is contained in:
“VeLiTi”
2025-01-30 11:43:37 +01:00
commit 6f1cc27ec4
823 changed files with 233091 additions and 0 deletions

131
admin/account.php Normal file
View File

@@ -0,0 +1,131 @@
<?php
defined('admin') or exit;
// Default account product values
$account = [
'email' => '',
'password' => '',
'role' => 'Member',
'first_name' => '',
'last_name' => '',
'address_street' => '',
'address_city' => '',
'address_state' => '',
'address_zip' => '',
'address_country' => '',
'registered' => date('Y-m-d\TH:i'),
'address_phone' => ''
];
if (isset($_GET['id'])) {
// Retrieve the account from the database
$stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$account = $stmt->fetch(PDO::FETCH_ASSOC);
// ID param exists, edit an existing account
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the account
$password = !empty($_POST['password']) ? password_hash($_POST['password'], PASSWORD_DEFAULT) : $account['password'];
$stmt = $pdo->prepare('UPDATE accounts SET email = ?, password = ?, first_name = ?, last_name = ?, address_street = ?, address_city = ?, address_state = ?, address_zip = ?, address_country = ?, role = ?, registered = ?, address_phone = ? WHERE id = ?');
$stmt->execute([ $_POST['email'], $password, $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], $_POST['role'], date('Y-m-d H:i:s', strtotime($_POST['registered'])), $_POST['address_phone'],$_GET['id'] ]);
header('Location: index.php?page=accounts&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the account
$stmt = $pdo->prepare('DELETE FROM accounts WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
header('Location: index.php?page=accounts&success_msg=3');
exit;
}
} else {
// Create a new account
$page = 'Create';
if (isset($_POST['submit'])) {
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO accounts (email,password,first_name,last_name,address_street,address_city,address_state,address_zip,address_country,role,registered, address_phone) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([ $_POST['email'], $password, $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], $_POST['role'], date('Y-m-d H:i:s', strtotime($_POST['registered'])), $_POST['address_phone'] ]);
header('Location: index.php?page=accounts&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Account', 'accounts', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Account</h2>
<a href="index.php?page=accounts" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this account?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="tabs">
<a href="#" class="active">General</a>
<a href="#">Shipping Address</a>
</div>
<div class="content-block tab-content active">
<div class="form responsive-width-100">
<label for="email"><i class="required">*</i> Email</label>
<input id="email" type="email" name="email" placeholder="Email" value="<?=htmlspecialchars($account['email'], ENT_QUOTES)?>" required>
<label for="password"><?=$page == 'Edit' ? 'New ' : ''?>Password</label>
<input type="text" id="password" name="password" placeholder="<?=$page == 'Edit' ? 'New ' : ''?>Password" value=""<?=$page == 'Edit' ? '' : ' required'?>>
<label for="first_name">First Name</label>
<input id="first_name" type="text" name="first_name" placeholder="Joe" value="<?=htmlspecialchars($account['first_name'], ENT_QUOTES)?>">
<label for="last_name">Last Name</label>
<input id="last_name" type="text" name="last_name" placeholder="Bloggs" value="<?=htmlspecialchars($account['last_name'], ENT_QUOTES)?>">
<label for="role"><i class="required">*</i> Role</label>
<select id="role" name="role" required>
<option value="Member"<?=$account['role']=='Member'?' selected':''?>>Member</option>
<option value="Admin"<?=$account['role']=='Admin'?' selected':''?>>Admin</option>
</select>
<label for="registered"><i class="required">*</i> Registered</label>
<input id="registered" type="datetime-local" name="registered" value="<?=date('Y-m-d\TH:i', strtotime($account['registered']))?>" required>
</div>
</div>
<div class="content-block tab-content">
<div class="form responsive-width-100">
<label for="address_street">Address Street</label>
<input id="address_street" type="text" name="address_street" placeholder="" value="<?=htmlspecialchars($account['address_street'], ENT_QUOTES)?>">
<label for="address_city">Address City</label>
<input id="address_city" type="text" name="address_city" placeholder="" value="<?=htmlspecialchars($account['address_city'], ENT_QUOTES)?>">
<label for="address_state">Address State</label>
<input id="address_state" type="text" name="address_state" placeholder="" value="<?=htmlspecialchars($account['address_state'], ENT_QUOTES)?>">
<label for="address_zip">Address Zip</label>
<input id="address_zip" type="text" name="address_zip" placeholder="" value="<?=htmlspecialchars($account['address_zip'], ENT_QUOTES)?>">
<label for="address_phone">Phone</label>
<input id="address_phone" type="text" name="address_phone" placeholder="" value="<?=htmlspecialchars($account['address_phone'], ENT_QUOTES)?>">
<label for="address_country">Country</label>
<select id="address_country" name="address_country" required>
<?php foreach(get_countries() as $country): ?>
<option value="<?=$country?>"<?=$country==$account['address_country']?' selected':''?>><?=$country?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</form>
<?=template_admin_footer()?>

138
admin/accounts.php Normal file
View File

@@ -0,0 +1,138 @@
<?php
defined('admin') or exit;
// Retrieve the GET request parameters (if specified)
$pagination_page = isset($_GET['pagination_page']) ? $_GET['pagination_page'] : 1;
$search = isset($_GET['search']) ? $_GET['search'] : '';
// Order by column
$order = isset($_GET['order']) && $_GET['order'] == 'DESC' ? 'DESC' : 'ASC';
// Add/remove columns to the whitelist array
$order_by_whitelist = ['id','email','first_name','role','registered'];
$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], $order_by_whitelist) ? $_GET['order_by'] : 'id';
// Number of results per pagination page
$results_per_page = 20;
// Declare query param variables
$param1 = ($pagination_page - 1) * $results_per_page;
$param2 = $results_per_page;
$param3 = '%' . $search . '%';
// SQL where clause
$where = '';
$where .= $search ? 'WHERE (a.email LIKE :search OR a.first_name LIKE :search OR a.last_name LIKE :search) ' : '';
// Retrieve the total number of products
$stmt = $pdo->prepare('SELECT COUNT(*) AS total FROM accounts a ' . $where);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
$accounts_total = $stmt->fetchColumn();
// SQL query to get all products from the "products" table
$stmt = $pdo->prepare('SELECT a.*, count(t.id) AS orders FROM accounts a LEFT JOIN transactions t ON t.account_id = a.id ' . $where . ' GROUP BY a.id, a.email, a.password, a.role, a.first_name, a.last_name, a.address_street, a.address_city, a.address_state, a.address_zip, a.address_country, a.registered ORDER BY ' . $order_by . ' ' . $order . ' LIMIT :start_results,:num_results');
// Bind params
$stmt->bindParam('start_results', $param1, PDO::PARAM_INT);
$stmt->bindParam('num_results', $param2, PDO::PARAM_INT);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
// Retrieve query results
$accounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Account created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Account updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Account deleted successfully!';
}
}
// Determine the URL
$url = 'index.php?page=accounts&search=' . $search;
?>
<?=template_admin_header('Accounts', 'accounts', 'view')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-users"></i>
<div class="txt">
<h2>Accounts</h2>
<p>View, create, and edit accounts.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=account" class="btn">Create Account</a>
<form action="" method="get">
<input type="hidden" name="page" value="accounts">
<div class="search">
<label for="search">
<input id="search" type="text" name="search" placeholder="Search account..." value="<?=htmlspecialchars($search, ENT_QUOTES)?>" class="responsive-width-100">
<i class="fas fa-search"></i>
</label>
</div>
</form>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=id'?>">#<?php if ($order_by=='id'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=email'?>">Email<?php if ($order_by=='email'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=first_name'?>">Name<?php if ($order_by=='first_name'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden">Address</td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=role'?>">Role<?php if ($order_by=='role'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden">Orders Placed</td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=registered'?>">Registered Date<?php if ($order_by=='registered'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($accounts)): ?>
<tr>
<td colspan="8" style="text-align:center;">There are no accounts</td>
</tr>
<?php else: ?>
<?php foreach ($accounts as $account): ?>
<tr>
<td class="responsive-hidden"><?=$account['id']?></td>
<td><?=htmlspecialchars($account['email'], ENT_QUOTES)?></td>
<td><?=htmlspecialchars($account['first_name'], ENT_QUOTES)?> <?=htmlspecialchars($account['last_name'], ENT_QUOTES)?></td>
<td class="responsive-hidden">
<?=htmlspecialchars($account['address_street'], ENT_QUOTES)?><?=$account['address_street']?', ':''?>
<?=htmlspecialchars($account['address_city'], ENT_QUOTES)?><?=$account['address_city']?', ':''?>
<?=htmlspecialchars($account['address_state'], ENT_QUOTES)?><?=$account['address_state']?', ':''?>
<?=htmlspecialchars($account['address_zip'], ENT_QUOTES)?><?=$account['address_zip']?', ':''?>
<?=htmlspecialchars($account['address_country'], ENT_QUOTES)?>
</td>
<td class="responsive-hidden"><?=$account['role']?></td>
<td class="responsive-hidden"><a href="index.php?page=orders&account_id=<?=$account['id']?>" class="link1"><?=number_format($account['orders'])?></a></td>
<td class="responsive-hidden"><?=date('F j, Y', strtotime($account['registered']))?></td>
<td><a href="index.php?page=account&id=<?=$account['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="pagination">
<?php if ($pagination_page > 1): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page-1?>&order=<?=$order?>&order_by=<?=$order_by?>">Prev</a>
<?php endif; ?>
<span>Page <?=$pagination_page?> of <?=ceil($accounts_total / $results_per_page) == 0 ? 1 : ceil($accounts_total / $results_per_page)?></span>
<?php if ($pagination_page * $results_per_page < $accounts_total): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page+1?>&order=<?=$order?>&order_by=<?=$order_by?>">Next</a>
<?php endif; ?>
</div>
<?=template_admin_footer()?>

2153
admin/admin.css Normal file

File diff suppressed because one or more lines are too long

919
admin/admin.js Normal file
View File

@@ -0,0 +1,919 @@
'use strict';
const aside = document.querySelector('aside'), main = document.querySelector('main'), header = document.querySelector('header');
const asideStyle = window.getComputedStyle(aside);
if (localStorage.getItem('admin_menu') == 'closed') {
aside.classList.add('closed', 'responsive-hidden');
main.classList.add('full');
header.classList.add('full');
}
document.querySelector('.responsive-toggle').onclick = event => {
event.preventDefault();
if (asideStyle.display == 'none') {
aside.classList.remove('closed', 'responsive-hidden');
main.classList.remove('full');
header.classList.remove('full');
localStorage.setItem('admin_menu', '');
} else {
aside.classList.add('closed', 'responsive-hidden');
main.classList.add('full');
header.classList.add('full');
localStorage.setItem('admin_menu', 'closed');
}
};
document.querySelectorAll('.tabs a').forEach((element, index) => {
element.onclick = event => {
event.preventDefault();
document.querySelectorAll('.tabs a').forEach(element => element.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach((element2, index2) => {
if (index == index2) {
element.classList.add('active');
element2.style.display = 'block';
} else {
element2.style.display = 'none';
}
});
};
});
if (document.querySelector('.filters a')) {
let filtersList = document.querySelector('.filters .list');
let filtersListStyle = window.getComputedStyle(filtersList);
document.querySelector('.filters a').onclick = event => {
event.preventDefault();
if (filtersListStyle.display == 'none') {
filtersList.style.display = 'flex';
} else {
filtersList.style.display = 'none';
}
};
document.onclick = event => {
if (!event.target.closest('.filters')) {
filtersList.style.display = 'none';
}
};
}
document.querySelectorAll('.msg').forEach(element => {
element.querySelector('.fa-times').onclick = () => {
element.remove();
history.replaceState && history.replaceState(null, '', location.pathname + location.search.replace(/[\?&]success_msg=[^&]+/, '').replace(/^&/, '?') + location.hash);
history.replaceState && history.replaceState(null, '', location.pathname + location.search.replace(/[\?&]error_msg=[^&]+/, '').replace(/^&/, '?') + location.hash);
};
});
if (location.search.includes('success_msg') || location.search.includes('error_msg')) {
history.replaceState && history.replaceState(null, '', location.pathname + location.search.replace(/[\?&]success_msg=[^&]+/, '').replace(/^&/, '?') + location.hash);
history.replaceState && history.replaceState(null, '', location.pathname + location.search.replace(/[\?&]error_msg=[^&]+/, '').replace(/^&/, '?') + location.hash);
}
document.body.addEventListener('click', event => {
if (!event.target.closest('.multiselect')) {
document.querySelectorAll('.multiselect .list').forEach(element => element.style.display = 'none');
}
});
document.querySelectorAll('.multiselect').forEach(element => {
let updateList = () => {
element.querySelectorAll('.item').forEach(item => {
element.querySelectorAll('.list span').forEach(newItem => {
if (item.dataset.value == newItem.dataset.value) {
newItem.style.display = 'none';
}
});
item.querySelector('.remove').onclick = () => {
element.querySelector('.list span[data-value="' + item.dataset.value + '"]').style.display = 'flex';
item.querySelector('.remove').parentElement.remove();
};
});
if (element.querySelectorAll('.item').length > 0) {
element.querySelector('.search').placeholder = '';
}
};
element.onclick = () => element.querySelector('.search').focus();
element.querySelector('.search').onfocus = () => element.querySelector('.list').style.display = 'flex';
element.querySelector('.search').onclick = () => element.querySelector('.list').style.display = 'flex';
element.querySelector('.search').onkeyup = () => {
element.querySelector('.list').style.display = 'flex';
element.querySelectorAll('.list span').forEach(item => {
item.style.display = item.innerText.toLowerCase().includes(element.querySelector('.search').value.toLowerCase()) ? 'flex' : 'none';
});
updateList();
};
element.querySelectorAll('.list span').forEach(item => item.onclick = () => {
item.style.display = 'none';
let html = `
<span class="item" data-value="${item.dataset.value}">
<i class="remove">&times;</i>${item.innerText}
<input type="hidden" name="${element.dataset.name}" value="${item.dataset.value}">
</span>
`;
if (element.querySelector('.item')) {
let ele = element.querySelectorAll('.item');
ele = ele[ele.length-1];
ele.insertAdjacentHTML('afterend', html);
} else {
element.insertAdjacentHTML('afterbegin', html);
}
element.querySelector('.search').value = '';
updateList();
});
updateList();
});
const modal = options => {
let element;
if (document.querySelector(options.element)) {
element = document.querySelector(options.element);
} else if (options.modalTemplate) {
document.body.insertAdjacentHTML('beforeend', options.modalTemplate());
element = document.body.lastElementChild;
}
options.element = element;
options.open = obj => {
element.style.display = 'flex';
element.getBoundingClientRect();
element.classList.add('open');
if (options.onOpen) options.onOpen(obj);
};
options.close = obj => {
if (options.onClose) {
let returnCloseValue = options.onClose(obj);
if (returnCloseValue !== false) {
element.style.display = 'none';
element.classList.remove('open');
element.remove();
}
} else {
element.style.display = 'none';
element.classList.remove('open');
element.remove();
}
};
if (options.state == 'close') {
options.close({ source: element, button: null });
} else if (options.state == 'open') {
options.open({ source: element });
}
element.querySelectorAll('.dialog-close').forEach(e => {
e.onclick = event => {
event.preventDefault();
options.close({ source: element, button: e });
};
});
return options;
};
const openMediaLibrary = options => modal({
media: [],
state: 'open',
modalTemplate: function() {
return `
<div class="dialog large media-library-modal">
<div class="content">
<h3 class="heading">Media Library<span class="dialog-close">&times;</span></h3>
<div class="media">
<div class="list">
<div class="list-header">
<a href="#" class="btn small green upload-media">Upload</a>
<input class="search-media" type="text" placeholder="Search...">
</div>
<div class="loader"></div>
</div>
<div class="details">
<p>No media selected.</p>
</div>
</div>
<div class="footer pad-5">
<a href="#" class="btn dialog-close save">${options.buttonText ? options.buttonText : 'Save'}</a>
</div>
</div>
</div>
`;
},
detailsTemplate: function(media, img) {
return `
<form class="media-details-form" method="post" action="index.php?page=api&action=media&id=${media.id}">
<h3>Media Details</h3>
<a href="${img}" target="_blank"><img src="${img}" alt="${media.caption}"></a>
<label for="title">Title</label>
<input id="title" type="text" name="title" value="${media.title}">
<label for="caption">Caption</label>
<input id="caption" type="text" name="caption" value="${media.caption}">
<label for="full_path">Full Path</label>
<input id="full_path" type="text" name="full_path" value="${media.full_path}">
<label for="date_uploaded">Date Uploaded</label>
<input id="date_uploaded" type="datetime-local" name="date_uploaded" value="${media.date_uploaded.replace(' ', 'T')}">
<div class="media-links">
<a href="#" class="link1 save-media">Save</a> <a href="#" class="link2 delete-media">Delete</a>
</div>
</form>
`;
},
selectMedia: function(id) {
for (let i = 0; i < this.media.length; i++) {
if (this.media[i].id == id) {
this.media[i].selected = true;
this.media[i].element.classList.add('selected');
}
}
},
unselectMedia: function(id) {
for (let i = 0; i < this.media.length; i++) {
if (this.media[i].id == id) {
this.media[i].selected = false;
this.media[i].element.classList.remove('selected');
}
}
},
getAllSelectedMedia: function() {
return this.media.filter(media => media.selected);
},
populateMedia: function(data) {
data = data ? data : this.media;
if (this.media.length > 0) {
document.querySelectorAll('.media-library-modal a.media-image').forEach(element => element.remove());
}
for (let i = 0; i < data.length; i++) {
let img = document.createElement('img');
img.loading = 'lazy';
img.src = '../' + data[i].full_path;
let a = document.createElement('a');
a.classList.add('media-image');
a.dataset.index = i;
a.dataset.id = data[i].id;
a.append(img);
a.onclick = event => {
event.preventDefault();
a.classList.toggle('selected');
if (a.classList.contains('selected')) {
this.selectMedia(data[i].id);
document.querySelector('.media-library-modal .media .details').innerHTML = this.detailsTemplate(data[i], img.src);
} else if (document.querySelector('.media-library-modal a.media-image.selected')) {
this.unselectMedia(data[i].id);
let selectedMedia = document.querySelector('.media-library-modal a.media-image.selected');
document.querySelector('.media-library-modal .media .details').innerHTML = this.detailsTemplate(data[selectedMedia.dataset.index], selectedMedia.querySelector('img').src);
} else {
this.unselectMedia(data[i].id);
document.querySelector('.media-library-modal .media .details').innerHTML = `<p>No media selected.</p>`;
}
document.querySelectorAll('.media-library-modal .media .details input').forEach(element => element.onkeyup = () => document.querySelector('.media-library-modal .save-media').style.display = 'inline-flex');
if (document.querySelector('.media-library-modal .save-media')) {
let mediaDetailsForm = document.querySelector('.media-library-modal .media-details-form');
document.querySelector('.media-library-modal .save-media').onclick = event => {
event.preventDefault();
fetch(mediaDetailsForm.action, {
method: 'POST',
body: new FormData(mediaDetailsForm)
}).then(response => response.json()).then(newData => {
this.media[i].title = newData[i].title;
this.media[i].caption = newData[i].caption;
this.media[i].full_path = newData[i].full_path;
this.media[i].date_uploaded = newData[i].date_uploaded;
data[i].title = newData[i].title;
data[i].caption = newData[i].caption;
data[i].full_path = newData[i].full_path;
data[i].date_uploaded = newData[i].date_uploaded;
document.querySelector('.media-library-modal .save-media').style.display = 'none';
});
};
document.querySelector('.media-library-modal .delete-media').onclick = event => {
event.preventDefault();
if (confirm('Are you sure you want to delete this media?')) {
fetch(mediaDetailsForm.action + '&delete=true').then(response => response.json()).then(newData => {
for (let j = 0; j < this.media.length; j++) {
for (let k = 0; k < newData.length; k++) {
if (this.media[j].id == newData[k].id && this.media[j].selected) {
newData[k].selected = true;
}
}
}
this.media = newData;
document.querySelector('.media-library-modal .media .details').innerHTML = `<p>No media selected.</p>`;
this.populateMedia();
});
}
};
}
};
data[i].element = a;
document.querySelector('.media-library-modal .media .list').append(a);
}
this.getAllSelectedMedia().forEach(media => {
if (media.selected) this.selectMedia(media.id);
});
if (document.querySelector('.media-library-modal .media .loader')) {
document.querySelector('.media-library-modal .media .loader').remove();
}
},
onOpen: function() {
fetch('index.php?page=api&action=media', { cache: 'no-store' }).then(response => response.json()).then(data => {
this.media = data;
this.populateMedia();
if (options.onMediaLoad) options.onMediaLoad();
});
document.querySelector('.media-library-modal .upload-media').onclick = event => {
event.preventDefault();
let input = document.createElement('input');
input.type = 'file';
input.multiple = 'multiple';
input.accept = 'image/*';
input.onchange = event => {
document.querySelector('.media-library-modal .upload-media').innerHTML = '<div class="loader"></div>';
let form = new FormData();
for (let i = 0; i < event.target.files.length; i++) {
form.append('file_' + i, event.target.files[i]);
}
form.append('total_files', event.target.files.length);
fetch('index.php?page=api&action=media', {
method: 'POST',
body: form
}).then(response => response.json()).then(data => {
for (let j = 0; j < this.media.length; j++) {
for (let k = 0; k < data.length; k++) {
if (this.media[j].id == data[k].id && this.media[j].selected) {
data[k].selected = true;
}
}
}
this.media = data;
this.populateMedia();
document.querySelector('.media-library-modal .upload-media').innerHTML = 'Upload';
});
};
input.click();
};
document.querySelector('.media-library-modal .search-media').onchange = () => {
document.querySelector('.media-library-modal .media .details').innerHTML = `<p>No media selected.</p>`;
this.populateMedia(this.media.filter(media => {
return media.title.toLowerCase().includes(document.querySelector('.media-library-modal .search-media').value.toLowerCase())
|| media.caption.toLowerCase().includes(document.querySelector('.media-library-modal .search-media').value.toLowerCase());
}));
this.getAllSelectedMedia().forEach(media => {
if (media.selected) this.selectMedia(media.id);
});
};
},
onClose: function(event) {
if (options.onSave && event && event.button && event.button.classList.contains('save')) options.onSave(this.getAllSelectedMedia());
if (options.onClose) options.onClose();
}
});
const openOptionsModal = options => modal({
state: 'open',
selectedOptionContainer: null,
selectedOptionType: null,
modalTemplate: function() {
return `
<div class="dialog medium options-modal">
<div class="content">
<h3 class="heading">Add Option<span class="dialog-close">&times;</span></h3>
<div class="body">
<div class="option-header">
<input type="text" class="option-title" placeholder="Title" data-default-title="">
<select class="option-type">
<option value="" disabled selected>-- select type --</option>
<option value="0">Select</option>
<option value="1">Radio</option>
<option value="2">Checkbox</option>
<option value="3">Text</option>
<option value="4">Date & Time</option>
</select>
<label>
<input type="checkbox" class="option-required">Required
</label>
</div>
${this.optionTemplate('select')}
${this.optionTemplate('radio')}
${this.optionTemplate('checkbox')}
${this.optionTemplate('text')}
${this.optionTemplate('datetime')}
</div>
<div class="footer pad-5">
<a href="#" class="btn dialog-close save">${options.buttonText ? options.buttonText : 'Save'}</a>
</div>
</div>
</div>
`;
},
optionTemplate: function(type) {
return `
<div class="option-content" data-type="${type}">
<div class="table">
<table>
<thead>
<tr>
${type == 'text' || type == 'datetime' ? '<td>Default Value</td>' : '<td>Name</td>'}
${type == 'text' || type == 'datetime' ? '' : '<td>Quantity</td>'}
<td>Price</td>
<td>Weight</td>
${type == 'text' || type == 'datetime' ? '' : '<td></td>'}
</tr>
</thead>
<tbody>
${this.optionValueTemplate(type)}
</tbody>
</table>
</div>
${type == 'text' || type == 'datetime' ? '' : '<a href="#" class="add-option-value-btn"><i class="fa-solid fa-plus"></i>Add Option Value</a>'}
</div>
`;
},
optionValueTemplate: function(type, option) {
if (type == 'text' || type == 'datetime') {
return `
<tr class="option-value">
<td>
${type == 'text' ? '<input type="text" placeholder="Value" class="name" value="' + (option ? option.name : '') + '">' : '<input type="datetime-local" class="name" value="' + (option ? option.name : '') + '">'}
<input type="hidden" class="quantity" value="-1">
</td>
<td>
<div class="input-group">
<select class="modifier price-mod">
<option value="add"${option && option.price_modifier == 'add' ? ' selected' : ''}>+</option>
<option value="subtract"${option && option.price_modifier == 'subtract' ? ' selected' : ''}>-</option>
</select>
<input type="number" class="price" placeholder="Price" min="0" step=".01" value="${option ? option.price : ''}">
</div>
</td>
<td>
<div class="input-group">
<select class="modifier weight-mod">
<option value="add"${option && option.weight_modifier == 'add' ? ' selected' : ''}>+</option>
<option value="subtract"${option && option.weight_modifier == 'subtract' ? ' selected' : ''}>-</option>
</select>
<input type="number" class="weight" placeholder="Weight" min="0" value="${option ? option.weight : ''}">
</div>
</td>
</tr>
`;
} else {
return `
<tr class="option-value">
<td><input type="text" placeholder="Name" class="name" value="${option ? option.name : ''}"></td>
<td><input type="number" placeholder="Quantity" class="quantity" title="-1 = unlimited" value="${option ? option.quantity : ''}"></td>
<td>
<div class="input-group">
<select class="modifier price-mod">
<option value="add"${option && option.price_modifier == 'add' ? ' selected' : ''}>+</option>
<option value="subtract"${option && option.price_modifier == 'subtract' ? ' selected' : ''}>-</option>
</select>
<input type="number" class="price" placeholder="Price" min="0" step=".01" value="${option ? option.price : ''}">
</div>
</td>
<td>
<div class="input-group">
<select class="modifier weight-mod">
<option value="add"${option && option.weight_modifier == 'add' ? ' selected' : ''}>+</option>
<option value="subtract"${option && option.weight_modifier == 'subtract' ? ' selected' : ''}>-</option>
</select>
<input type="number" class="weight" placeholder="Weight" min="0" value="${option ? option.weight : ''}">
</div>
</td>
<td><i class="fa-solid fa-xmark remove-option-value"></i></td>
</tr>
`;
}
},
onOpen: function() {
this.element.querySelector('.option-type').onchange = () => {
this.element.querySelectorAll('.option-content').forEach(element => element.style.display = 'none');
this.element.querySelectorAll('.option-content')[this.element.querySelector('.option-type').value].style.display = 'flex';
this.selectedOptionContainer = this.element.querySelectorAll('.option-content')[this.element.querySelector('.option-type').value];
this.selectedOptionType = this.selectedOptionContainer.dataset.type;
if (this.selectedOptionContainer.querySelector('.add-option-value-btn')) {
this.selectedOptionContainer.querySelector('.add-option-value-btn').onclick = event => {
event.preventDefault();
this.selectedOptionContainer.querySelector('tbody').insertAdjacentHTML('beforeend', this.optionValueTemplate());
this.element.querySelectorAll('.remove-option-value').forEach(element => element.onclick = () => element.closest('.option-value').remove());
};
}
};
if (options.options && options.options.length > 0) {
this.element.querySelector('.option-title').value = options.options[0].title;
this.element.querySelector('.option-title').dataset.defaultTitle = options.options[0].title;
this.element.querySelector('.option-required').checked = parseInt(options.options[0].required) ? true : false;
this.element.querySelector('.option-type').value = options.options[0].type.replace('select', '0').replace('radio', '1').replace('checkbox', '2').replace('text', '3').replace('datetime', '4');
this.element.querySelector('.option-type').onchange();
this.selectedOptionContainer.querySelector('tbody').innerHTML = '';
options.options.forEach(option => {
this.selectedOptionContainer.querySelector('tbody').insertAdjacentHTML('beforeend', this.optionValueTemplate(option.type, option));
});
this.element.querySelectorAll('.remove-option-value').forEach(element => element.onclick = () => element.closest('.option-value').remove());
}
},
onClose: function(event) {
if (options.onSave && event && event.button && event.button.classList.contains('save') && this.selectedOptionType != null) {
if (!this.element.querySelector('.option-title').value) {
this.element.querySelector('.option-title').setCustomValidity('Please enter the option title!');
this.element.querySelector('.option-title').reportValidity();
return false;
}
if (options.reservedTitles.includes(this.element.querySelector('.option-title').value.toLowerCase()) && this.element.querySelector('.option-title').value.toLowerCase() != this.element.querySelector('.option-title').dataset.defaultTitle.toLowerCase()) {
this.element.querySelector('.option-title').setCustomValidity('Title already exists!');
this.element.querySelector('.option-title').reportValidity();
return false;
}
let productOptions = [];
this.selectedOptionContainer.querySelectorAll('.option-value').forEach(optionValue => {
productOptions.push({
title: this.element.querySelector('.option-title').value,
name: optionValue.querySelector('.name').value,
quantity: optionValue.querySelector('.quantity').value,
price: optionValue.querySelector('.price').value,
price_modifier: optionValue.querySelector('.price-mod').value,
weight: optionValue.querySelector('.weight').value,
weight_modifier: optionValue.querySelector('.weight-mod').value,
type: this.selectedOptionType,
required: this.element.querySelector('.option-required').checked ? 1 : 0
});
});
options.onSave(productOptions);
}
}
});
const initProduct = () => {
let productMediaHandler = () => {
document.querySelectorAll('.media-position i').forEach(element => element.onclick = event => {
event.preventDefault();
let mediaElement = element.closest('.product-media');
if (element.classList.contains('move-up') && mediaElement.previousElementSibling) {
mediaElement.parentNode.insertBefore(mediaElement, mediaElement.previousElementSibling);
}
if (element.classList.contains('move-down') && mediaElement.nextElementSibling) {
mediaElement.parentNode.insertBefore(mediaElement.nextElementSibling, mediaElement);
}
if (element.classList.contains('media-delete') && confirm('Are you sure you want to delete this media?')) {
mediaElement.remove();
}
document.querySelectorAll('.product-media').forEach((element, index) => {
element.querySelector('.media-index').innerHTML = index+1;
element.querySelector('.input-media-position').value = index+1;
});
});
};
productMediaHandler();
document.querySelector('.open-media-library-modal').onclick = event => {
event.preventDefault();
openMediaLibrary({
multiSelect: true,
buttonText: 'Add',
onSave: function(media) {
if (media && document.querySelector('.no-images-msg')) {
document.querySelector('.no-images-msg').remove();
}
media.forEach(m => {
let index = document.querySelectorAll('.product-media').length;
document.querySelector('.product-media-container').insertAdjacentHTML('beforeend', `
<div class="product-media">
<span class="media-index responsive-hidden">${index+1}</span>
<a class="media-img" href="../${m.full_path}" target="_blank">
<img src="../${m.full_path}">
</a>
<div class="media-text">
<h3 class="responsive-hidden">${m.title}</h3>
<p class="responsive-hidden">${m.caption}</p>
</div>
<div class="media-position">
<i class="fas fa-times media-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
<input type="hidden" class="input-media-id" name="media[]" value="${m.id}">
<input type="hidden" class="input-media-product-id" name="media_product_id[]" value="0">
<input type="hidden" class="input-media-position" name="media_position[]" value="${index+1}">
</div>
`);
});
productMediaHandler();
}
});
};
let productOptionsHandler = () => {
document.querySelectorAll('.option-position i').forEach(element => element.onclick = event => {
event.preventDefault();
let optionElement = element.closest('.product-option');
if (element.classList.contains('move-up') && optionElement.previousElementSibling) {
optionElement.parentNode.insertBefore(optionElement, optionElement.previousElementSibling);
}
if (element.classList.contains('move-down') && optionElement.nextElementSibling) {
optionElement.parentNode.insertBefore(optionElement.nextElementSibling, optionElement);
}
if (element.classList.contains('option-delete') && confirm('Are you sure you want to delete this option?')) {
optionElement.remove();
}
if (element.classList.contains('option-edit')) {
let options = [];
optionElement.querySelectorAll('.input-option-value').forEach(optionValue => {
options.push({
title: optionValue.querySelector('.input-option-title').value,
name: optionValue.querySelector('.input-option-name').value,
quantity: optionValue.querySelector('.input-option-quantity').value,
price: optionValue.querySelector('.input-option-price').value,
price_modifier: optionValue.querySelector('.input-option-price-modifier').value,
weight: optionValue.querySelector('.input-option-weight').value,
weight_modifier: optionValue.querySelector('.input-option-weight-modifier').value,
type: optionValue.querySelector('.input-option-type').value,
required: optionValue.querySelector('.input-option-required').value
});
});
openOptionsModal({
buttonText: 'Save',
reservedTitles: [...document.querySelectorAll('.product-option')].map(option => option.querySelector('.input-option-title').value.toLowerCase()),
options: options,
onSave: function(options) {
if (options.length > 0) {
let optionsHtml = '';
let optionsValuesHtml = '';
options.forEach(option => {
optionsHtml += `
<div class="input-option-value">
<input type="hidden" class="input-option-title" name="option_title[]" value="${option.title}">
<input type="hidden" class="input-option-name" name="option_name[]" value="${option.name}">
<input type="hidden" class="input-option-quantity" name="option_quantity[]" value="${option.quantity}">
<input type="hidden" class="input-option-price" name="option_price[]" value="${option.price}">
<input type="hidden" class="input-option-price-modifier" name="option_price_modifier[]" value="${option.price_modifier}">
<input type="hidden" class="input-option-weight" name="option_weight[]" value="${option.weight}">
<input type="hidden" class="input-option-weight-modifier" name="option_weight_modifier[]" value="${option.weight_modifier}">
<input type="hidden" class="input-option-type" name="option_type[]" value="${option.type}">
<input type="hidden" class="input-option-required" name="option_required[]" value="${option.required}">
<input type="hidden" class="input-option-position" name="option_position[]" value="${optionElement.querySelector('.input-option-position').value}">
</div>
`;
optionsValuesHtml += option.name + ', ';
})
optionElement.innerHTML = `
<span class="option-index responsive-hidden">${optionElement.querySelector('.option-index').innerHTML}</span>
<div class="option-text">
<h3>${options[0].title} (${options[0].type})</h3>
<p>${optionsValuesHtml.replace(/, $/, '')}</p>
</div>
<div class="option-position">
<i class="fas fa-pen option-edit"></i>
<i class="fas fa-times option-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
${optionsHtml}
`;
}
productOptionsHandler();
}
});
}
document.querySelectorAll('.product-option').forEach((element, index) => {
element.querySelector('.option-index').innerHTML = index+1;
element.querySelectorAll('.input-option-position').forEach(input => input.value = index+1);
});
});
};
productOptionsHandler();
document.querySelector('.open-options-modal').onclick = event => {
event.preventDefault();
openOptionsModal({
buttonText: 'Add',
reservedTitles: [...document.querySelectorAll('.product-option')].map(option => option.querySelector('.input-option-title').value.toLowerCase()),
onSave: function(options) {
if (options.length > 0) {
if (document.querySelector('.no-options-msg')) {
document.querySelector('.no-options-msg').remove();
}
let index = document.querySelectorAll('.product-option').length;
let optionsHtml = '';
let optionsValuesHtml = '';
options.forEach(option => {
optionsHtml += `
<div class="input-option-value">
<input type="hidden" class="input-option-title" name="option_title[]" value="${option.title}">
<input type="hidden" class="input-option-name" name="option_name[]" value="${option.name}">
<input type="hidden" class="input-option-quantity" name="option_quantity[]" value="${option.quantity}">
<input type="hidden" class="input-option-price" name="option_price[]" value="${option.price}">
<input type="hidden" class="input-option-price-modifier" name="option_price_modifier[]" value="${option.price_modifier}">
<input type="hidden" class="input-option-weight" name="option_weight[]" value="${option.weight}">
<input type="hidden" class="input-option-weight-modifier" name="option_weight_modifier[]" value="${option.weight_modifier}">
<input type="hidden" class="input-option-type" name="option_type[]" value="${option.type}">
<input type="hidden" class="input-option-required" name="option_required[]" value="${option.required}">
<input type="hidden" class="input-option-position" name="option_position[]" value="${index+1}">
</div>
`;
optionsValuesHtml += option.name + ', ';
})
document.querySelector('.product-options-container').insertAdjacentHTML('beforeend', `
<div class="product-option">
<span class="option-index responsive-hidden">${index+1}</span>
<div class="option-text">
<h3>${options[0].title} (${options[0].type})</h3>
<p>${optionsValuesHtml.replace(/, $/, '')}</p>
</div>
<div class="option-position">
<i class="fas fa-pen option-edit"></i>
<i class="fas fa-times option-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
${optionsHtml}
</div>
`);
}
productOptionsHandler();
}
});
};
let productDownloadsHandler = () => {
document.querySelectorAll('.download-position i').forEach(element => element.onclick = event => {
event.preventDefault();
let downloadElement = element.closest('.product-download');
if (element.classList.contains('move-up') && downloadElement.previousElementSibling) {
downloadElement.parentNode.insertBefore(downloadElement, downloadElement.previousElementSibling);
}
if (element.classList.contains('move-down') && downloadElement.nextElementSibling) {
downloadElement.parentNode.insertBefore(downloadElement.nextElementSibling, downloadElement);
}
if (element.classList.contains('download-delete') && confirm('Are you sure you want to delete this digital download?')) {
downloadElement.remove();
}
document.querySelectorAll('.product-download').forEach((element, index) => {
element.querySelector('.download-index').innerHTML = index+1;
element.querySelector('.input-download-position').value = index+1;
});
});
};
productDownloadsHandler();
document.querySelector('.open-downloads-modal').onclick = event => {
event.preventDefault();
modal({
state: 'open',
modalTemplate: function() {
return `
<div class="dialog downloads-modal">
<div class="content">
<h3 class="heading">Add Digital Download<span class="dialog-close">&times;</span></h3>
<div class="body">
<p>The file path must be relative to the shopping cart root directory.</p>
<label for="download-file-path">File Path</label>
<input id="download-file-path" type="text" class="download-file-path" placeholder="your_hidden_directory/your_file.zip" data-default-title="">
<p class="download-result"></p>
</div>
<div class="footer pad-5">
<a href="#" class="btn dialog-close save disabled">Add</a>
</div>
</div>
</div>
`;
},
onOpen: function() {
this.element.querySelector('.download-file-path').onchange = () => {
fetch('index.php?page=api&action=fileexists&path=' + this.element.querySelector('.download-file-path').value, { cache: 'no-store' }).then(response => response.json()).then(data => {
this.element.querySelector('.download-result').innerHTML = data.result;
if (data.result) {
this.element.querySelector('.save').classList.add('disabled');
} else {
this.element.querySelector('.save').classList.remove('disabled');
}
});
};
},
onClose: function(event) {
if (event && event.button && event.button.classList.contains('save')) {
if (event.button.classList.contains('disabled')) return false;
if (document.querySelector('.no-downloads-msg')) {
document.querySelector('.no-downloads-msg').remove();
}
let index = document.querySelectorAll('.product-download').length;
let file_path = document.querySelector('.download-file-path').value;
document.querySelector('.product-downloads-container').insertAdjacentHTML('beforeend', `
<div class="product-download">
<span class="download-index responsive-hidden">${index+1}</span>
<div class="download-text">
<h3 class="responsive-hidden">${file_path}</h3>
<p class="responsive-hidden"></p>
</div>
<div class="download-position">
<i class="fas fa-times download-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
<input type="hidden" class="input-download-file-path" name="download_file_path[]" value="${file_path}">
<input type="hidden" class="input-download-position" name="download_position[]" value="${index+1}">
</div>
`);
productDownloadsHandler();
}
}
});
};
};
const initMedia = () => {
let mediaHandler = () => {
document.querySelectorAll('.media .image').forEach(element => element.onclick = event => {
event.preventDefault();
modal({
state: 'open',
modalTemplate: function() {
return `
<div class="dialog edit-media-modal">
<div class="content">
<h3 class="heading">Edit Media<span class="dialog-close">&times;</span></h3>
<div class="body">
<form class="media-details-form" method="post" action="index.php?page=api&action=media&id=${element.dataset.id}">
<a href="../${element.dataset.fullPath}" target="_blank"><img src="../${element.dataset.fullPath}" alt=""></a>
<label for="title">Title</label>
<input id="title" type="text" name="title" value="${element.dataset.title}">
<label for="caption">Caption</label>
<input id="caption" type="text" name="caption" value="${element.dataset.caption}">
<label for="full_path">Full Path</label>
<input id="full_path" type="text" name="full_path" value="${element.dataset.fullPath}">
<label for="date_uploaded">Date Uploaded</label>
<input id="date_uploaded" type="datetime-local" name="date_uploaded" value="${element.dataset.dateUploaded}">
</form>
</div>
<div class="footer pad-5">
<a href="#" class="btn dialog-close save mar-right-1">Save</a>
<a href="#" class="btn dialog-close delete red">Delete</a>
</div>
</div>
</div>
`;
},
onClose: function(event) {
let mediaDetailsForm = this.element.querySelector('.media-details-form');
if (event && event.button && event.button.classList.contains('save')) {
fetch(mediaDetailsForm.action, {
method: 'POST',
body: new FormData(mediaDetailsForm)
}).then(response => response.json()).then(data => {
data.forEach(media => {
if (media.id == element.dataset.id) {
element.dataset.title = media.title;
element.dataset.caption = media.caption;
element.dataset.fullPath = media.fullPath;
element.dataset.dateUploaded = media.dateUploaded;
}
});
});
}
if (event && event.button && event.button.classList.contains('delete')) {
fetch(mediaDetailsForm.action + '&delete=true').then(response => response.json()).then(() => element.remove());
}
}
});
});
};
mediaHandler();
document.querySelector('.upload').onclick = event => {
event.preventDefault();
let input = document.createElement('input');
input.type = 'file';
input.multiple = 'multiple';
input.accept = 'image/*';
input.onchange = event => {
document.querySelector('.upload').innerHTML = '<div class="loader"></div>';
let total_files = event.target.files.length;
let form = new FormData();
for (let i = 0; i < total_files; i++) {
form.append('file_' + i, event.target.files[i]);
}
form.append('total_files', total_files);
fetch('index.php?page=api&action=media', {
method: 'POST',
body: form
}).then(response => response.json()).then(data => {
if (data) {
data.forEach((media, index) => {
if (index > total_files-1) return;
document.querySelector('.media').insertAdjacentHTML('afterbegin', `
<a href="#" class="image" data-id="${media.id}" data-title="${media.title}" data-caption="${media.caption}" data-date-uploaded="${media.date_uploaded.replace(' ', 'T')}" data-full-path="${media.full_path}">
<img src="../${media.full_path}" alt="${media.caption}" loading="lazy">
</a>
`);
});
}
document.querySelector('.upload').innerHTML = 'Upload';
mediaHandler();
});
};
input.click();
};
};
const initManageOrder = (products) => {
document.querySelector('.add-item').onclick = event => {
event.preventDefault();
document.querySelector('.manage-order-table tbody').insertAdjacentHTML('beforeend', `
<tr>
<td>
<input type="hidden" name="item_id[]" value="0">
<select name="item_product[]">
${products.map(product => '<option value="' + product.id + '">' + product.id + ' - ' + product.name + '</option>')}
</select>
</td>
<td><input name="item_price[]" type="number" placeholder="Price" step=".01"></td>
<td><input name="item_quantity[]" type="number" placeholder="Quantity"></td>
<td><input name="item_options[]" type="text" placeholder="Options"></td>
<td><i class="fa-solid fa-xmark delete-item"></i></td>
</tr>
`);
document.querySelectorAll('.delete-item').forEach(element => element.onclick = event => {
event.preventDefault();
element.closest('tr').remove();
});
if (document.querySelector('.no-order-items-msg')) {
document.querySelector('.no-order-items-msg').remove();
}
};
document.querySelectorAll('.delete-item').forEach(element => element.onclick = event => {
event.preventDefault();
element.closest('tr').remove();
});
};

1526
admin/admin.scss Normal file

File diff suppressed because it is too large Load Diff

76
admin/api.php Normal file
View File

@@ -0,0 +1,76 @@
<?php
defined('admin') or exit;
// Remove the time limit (for media uploads)
set_time_limit(0);
// Media Endpoint
if (isset($_GET['action']) && $_GET['action'] == 'media') {
// Upload media
if (isset($_POST['total_files']) && (int)$_POST['total_files'] > 0) {
// Iterate the uploaded files
for ($i = 0; $i < (int)$_POST['total_files']; $i++) {
// Ensure the file exists
if (isset($_FILES['file_' . $i]) && !empty($_FILES['file_' . $i]['tmp_name'])) {
$file_name = $_FILES['file_' . $i]['name'];
// Rename file if file exists with same name
$j = 1;
while (file_exists('../uploads/' . $file_name)) {
$file_name = $j . '-' . $_FILES['file_' . $i]['name'];
$j++;
}
$media_path = '../uploads/' . $file_name;
move_uploaded_file($_FILES['file_' . $i]['tmp_name'], $media_path);
$stmt = $pdo->prepare('INSERT INTO media (title, caption, date_uploaded, full_path) VALUES (?, ?, ?, ?)');
$stmt->execute([ $file_name, '', date('Y-m-d H:i:s'), substr($media_path, strlen('../')) ]);
}
}
}
// Select media
if (isset($_GET['id'])) {
// Retrieve media by id
$stmt = $pdo->prepare('SELECT * FROM media WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$media = $stmt->fetch(PDO::FETCH_ASSOC);
}
// Update media
if (isset($_GET['id'], $_POST['title'])) {
// Determine full path
$full_path = $media['full_path'];
// If captured path is different from original path, attempt to move and rename the file
if ($media['full_path'] != $_POST['full_path']) {
if (!is_dir(dirname('../' . $_POST['full_path']))) {
mkdir(dirname('../' . $_POST['full_path']), 0777, true);
}
if (rename('../' . $media['full_path'], '../' . $_POST['full_path'])) {
$full_path = $_POST['full_path'];
}
}
// Update media in the database
$stmt = $pdo->prepare('UPDATE media SET title = ?, caption = ?, date_uploaded = ?, full_path = ? WHERE id = ?');
$stmt->execute([ $_POST['title'], $_POST['caption'], date('Y-m-d H:i:s', strtotime($_POST['date_uploaded'])), $full_path, $_GET['id'] ]);
}
// Delete media
if (isset($_GET['id'], $_GET['delete'])) {
// Delete media file
unlink('../' . $media['full_path']);
// Delete from database
$stmt = $pdo->prepare('DELETE m, pm FROM media m LEFT JOIN products_media pm ON pm.media_id = m.id WHERE m.id = ?');
$stmt->execute([ $_GET['id'] ]);
}
// Get all media from database
$stmt = $pdo->prepare('SELECT *, DATE_FORMAT(date_uploaded, "%Y-%m-%d %H:%i") AS date_uploaded FROM media ORDER BY date_uploaded DESC');
$stmt->execute();
$media = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Output JSON
header('Content-Type: application/json; charset=utf-8');
// Encode results to JSON format
echo json_encode($media);
}
// Digital Downloads Endpoint
if (isset($_GET['action']) && $_GET['action'] == 'fileexists' && $_GET['path']) {
if (!file_exists('../' . $_GET['path']) || !is_file('../' . $_GET['path'])) {
echo '{"result":"The file does not exist!"}';
} else {
echo '{"result":""}';
}
}
?>

83
admin/categories.php Normal file
View File

@@ -0,0 +1,83 @@
<?php
defined('admin') or exit;
// SQL query to get all categories from the "categories" table
$stmt = $pdo->prepare('SELECT * FROM categories');
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Category created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Category updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Category deleted successfully!';
}
}
// Populate categories function
function admin_populate_categories($categories, $parent_id = 0, $n = 0) {
$html = '';
foreach ($categories as $category) {
if ($parent_id == $category['parent_id']) {
$html .= '
<tr>
<td><span style="padding-right:8px;color:#bbbec0;border-left:1px solid #bbbec0;padding-bottom:2px;">-' . str_repeat('----', $n) . '</span>' . $category['name'] . '</td>
<td><a href="index.php?page=category&id=' . $category['id'] . '" class="link1">Edit</a> (ID =' . $category['id'] . ') </td>
</tr>
';
$html .= admin_populate_categories($categories, $category['id'], $n+1);
}
}
return $html;
}
?>
<?=template_admin_header('Categories', 'categories')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-list-ul"></i>
<div class="txt">
<h2>Categories</h2>
<p>View, create, and edit categories.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=category" class="btn">Create Category</a>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td>Name</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($categories)): ?>
<tr>
<td colspan="8" style="text-align:center;">There are no categories</td>
</tr>
<?php else: ?>
<?=admin_populate_categories($categories)?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?=template_admin_footer()?>

85
admin/category.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
defined('admin') or exit;
// Default input category values
$category = [
'name' => '',
'parent_id' => 0,
'status' => 1
];
if (isset($_GET['id'])) {
// Retrieve all the categories
$stmt = $pdo->prepare('SELECT * FROM categories WHERE id != ?');
$stmt->execute([ $_GET['id'] ]);
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ID param exists, edit an existing category
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the category
$stmt = $pdo->prepare('UPDATE categories SET name = ?, parent_id = ?, status = ? WHERE id = ?');
$stmt->execute([ $_POST['name'], $_POST['parent_id'], $_POST['status'], $_GET['id'] ]);
header('Location: index.php?page=categories&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the category
$stmt = $pdo->prepare('DELETE c, pc FROM categories c LEFT JOIN products_categories pc ON pc.category_id = c.id WHERE c.id = ?');
$stmt->execute([ $_GET['id'] ]);
header('Location: index.php?page=categories&success_msg=3');
exit;
}
// Get the category from the database
$stmt = $pdo->prepare('SELECT * FROM categories WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$category = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
// Retrieve all the categories
$stmt = $pdo->prepare('SELECT * FROM categories');
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Create a new category
$page = 'Create';
if (isset($_POST['submit'])) {
$stmt = $pdo->prepare('INSERT INTO categories (name,parent_id,status) VALUES (?,?,?)');
$stmt->execute([ $_POST['name'], $_POST['parent_id'], $_POST['status'] ]);
header('Location: index.php?page=categories&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Category', 'categories', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Category</h2>
<a href="index.php?page=categories" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this category?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="content-block">
<div class="form responsive-width-100">
<label for="name"><i class="required">*</i> Name</label>
<input id="name" type="text" name="name" placeholder="Name" value="<?=$category['name']?>" required>
<label for="parent_id">Parent</label>
<select id="parent_id" name="parent_id">
<option value="0">(none)</option>
<?php foreach ($categories as $c): ?>
<option value="<?=$c['id']?>"<?=$c['id']==$category['parent_id']?' selected':''?>><?=$c['name']?></option>
<?php endforeach; ?>
</select>
<label for="status">Status</label>
<select id="status" name="status">
<option value="1"<?=$category['status']==1?' selected':''?>>Enabled</option>
<option value="0"<?=$category['status']==0?' selected':''?>>Disabled</option>
</select>
</div>
</div>
</form>
<?=template_admin_footer()?>

129
admin/dashboard.php Normal file
View File

@@ -0,0 +1,129 @@
<?php
defined('admin') or exit;
// SQL query that will get all orders and sort by the date created
$stmt = $pdo->prepare('SELECT t.*, COUNT(ti.id) AS total_products FROM transactions t JOIN transactions_items ti ON ti.txn_id = t.txn_id WHERE cast(t.created as DATE) = cast(now() as DATE) GROUP BY t.id, t.txn_id, t.payment_amount, t.payment_status, t.created, t.payer_email, t.first_name, t.last_name, t.address_street, t.address_city, t.address_state, t.address_zip, t.address_country, t.account_id, t.payment_method, t.discount_code, t.shipping_method, t.shipping_amount ORDER BY t.created DESC');
$stmt->execute();
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the orders statistics
$stmt = $pdo->prepare('SELECT SUM(payment_amount) AS earnings FROM transactions WHERE payment_status = "Completed" AND cast(created as DATE) = cast(now() as DATE)');
$stmt->execute();
$order_stats = $stmt->fetch(PDO::FETCH_ASSOC);
// Get the total number of accounts
$stmt = $pdo->prepare('SELECT COUNT(*) AS total FROM accounts');
$stmt->execute();
$accounts = $stmt->fetch(PDO::FETCH_ASSOC);
// Get the total number of products
$stmt = $pdo->prepare('SELECT COUNT(*) AS total FROM products');
$stmt->execute();
$products = $stmt->fetch(PDO::FETCH_ASSOC);
?>
<?=template_admin_header('Dashboard', 'dashboard')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-gauge-high"></i>
<div class="txt">
<h2>Dashboard</h2>
<p>View statistics, today's transactions, and more.</p>
</div>
</div>
</div>
<div class="dashboard">
<div class="content-block stat">
<div class="data">
<h3>New Orders</h3>
<p><?=number_format(count($orders))?></p>
</div>
<i class="fas fa-shopping-cart"></i>
<div class="footer">
<i class="fa-solid fa-rotate fa-xs"></i>Total orders for today
</div>
</div>
<div class="content-block stat">
<div class="data">
<h3>New Sales</h3>
<p><?=currency_code?><?=number_format($order_stats['earnings'] ?? 0, 2)?></p>
</div>
<i class="fas fa-coins"></i>
<div class="footer">
<i class="fa-solid fa-rotate fa-xs"></i>Total earnings for today
</div>
</div>
<div class="content-block stat">
<div class="data">
<h3>Total Accounts</h3>
<p><?=number_format($accounts['total'])?></p>
</div>
<i class="fas fa-users"></i>
<div class="footer">
<i class="fa-solid fa-rotate fa-xs"></i>Total accounts
</div>
</div>
<div class="content-block stat">
<div class="data">
<h3>Total Products</h3>
<p><?=number_format($products['total'])?></p>
</div>
<i class="fas fa-boxes"></i>
<div class="footer">
<i class="fa-solid fa-rotate fa-xs"></i>Total products
</div>
</div>
</div>
<div class="content-title">
<div class="title">
<i class="fa-regular fa-rectangle-list alt"></i>
<div class="txt">
<h2>Today's Transactions</h2>
<p>List of transactions for today.</p>
</div>
</div>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td>#</td>
<td>Customer</td>
<td class="responsive-hidden">Email</td>
<td class="responsive-hidden">Products</td>
<td>Total</td>
<td class="responsive-hidden">Method</td>
<td class="responsive-hidden">Status</td>
<td class="responsive-hidden">Date</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($orders)): ?>
<tr>
<td colspan="9" style="text-align:center;">There are no recent orders</td>
</tr>
<?php else: ?>
<?php foreach ($orders as $order): ?>
<tr>
<td><?=$order['id']?></td>
<td><?=htmlspecialchars($order['first_name'], ENT_QUOTES)?> <?=htmlspecialchars($order['last_name'], ENT_QUOTES)?></td>
<td class="responsive-hidden"><?=htmlspecialchars($order['payer_email'], ENT_QUOTES)?></td>
<td class="responsive-hidden"><?=$order['total_products']?></td>
<td><?=currency_code?><?=number_format($order['payment_amount'], 2)?></td>
<td class="responsive-hidden"><?=$order['payment_method']?></td>
<td class="responsive-hidden"><span class="status <?=strtolower($order['payment_status'])?>"><?=$order['payment_status']?></span></td>
<td class="responsive-hidden"><?=date('F j, Y', strtotime($order['created']))?></td>
<td><a href="index.php?page=order&id=<?=$order['id']?>" class="link1">View</a> <a href="index.php?page=order_manage&id=<?=$order['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?=template_admin_footer()?>

154
admin/discount.php Normal file
View File

@@ -0,0 +1,154 @@
<?php
defined('admin') or exit;
// Default input discount values
$discount = [
'category_ids' => '',
'product_ids' => '',
'discount_code' => '',
'discount_type' => 'Percentage',
'discount_value' => 0,
'start_date' => date('Y-m-d\TH:i'),
'end_date' => date('Y-m-d\TH:i', strtotime('+1 month', strtotime(date('Y-m-d\TH:i')))),
'categories' => [],
'products' => []
];
$types = ['Percentage', 'Fixed'];
// Get all the categories from the database
$stmt = $pdo->query('SELECT * FROM categories');
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get all the products from the database
$stmt = $pdo->query('SELECT * FROM products');
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (isset($_GET['id'])) {
// ID param exists, edit an existing discount
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the discount
$categories_list = isset($_POST['categories']) ? implode(',', $_POST['categories']) : '';
$products_list = isset($_POST['products']) ? implode(',', $_POST['products']) : '';
$stmt = $pdo->prepare('UPDATE discounts SET category_ids = ?, product_ids = ?, discount_code = ?, discount_type = ?, discount_value = ?, start_date = ?, end_date = ? WHERE id = ?');
$stmt->execute([ $categories_list, $products_list, $_POST['discount_code'], $_POST['discount_type'], $_POST['discount_value'], date('Y-m-d H:i:s', strtotime($_POST['start_date'])), date('Y-m-d H:i:s', strtotime($_POST['end_date'])), $_GET['id'] ]);
// Remove session discount code
if (isset($_SESSION['discount'])) {
unset($_SESSION['discount']);
}
header('Location: index.php?page=discounts&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the discount
$stmt = $pdo->prepare('DELETE FROM discounts WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
// Remove session discount code
if (isset($_SESSION['discount'])) {
unset($_SESSION['discount']);
}
header('Location: index.php?page=discounts&success_msg=3');
exit;
}
// Get the discount from the database
$stmt = $pdo->prepare('SELECT * FROM discounts WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$discount = $stmt->fetch(PDO::FETCH_ASSOC);
// Get the discount categories
$stmt = $pdo->prepare('SELECT c.name, c.id FROM discounts d JOIN categories c ON FIND_IN_SET(c.id, d.category_ids) WHERE d.id = ?');
$stmt->execute([ $_GET['id'] ]);
$discount['categories'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the discount products
$stmt = $pdo->prepare('SELECT p.name, p.id FROM discounts d JOIN products p ON FIND_IN_SET(p.id, d.product_ids) WHERE d.id = ?');
$stmt->execute([ $_GET['id'] ]);
$discount['products'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
// Create a new discount
$page = 'Create';
if (isset($_POST['submit'])) {
$categories_list = isset($_POST['categories']) ? implode(',', $_POST['categories']) : '';
$products_list = isset($_POST['products']) ? implode(',', $_POST['products']) : '';
$stmt = $pdo->prepare('INSERT INTO discounts (category_ids,product_ids,discount_code,discount_type,discount_value,start_date,end_date) VALUES (?,?,?,?,?,?,?)');
$stmt->execute([ $categories_list, $products_list, $_POST['discount_code'], $_POST['discount_type'], $_POST['discount_value'], date('Y-m-d H:i:s', strtotime($_POST['start_date'])), date('Y-m-d H:i:s', strtotime($_POST['end_date'])) ]);
// Remove session discount code
if (isset($_SESSION['discount'])) {
unset($_SESSION['discount']);
}
header('Location: index.php?page=discounts&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Discount', 'discounts', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Discount</h2>
<a href="index.php?page=discounts" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this discount?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="content-block">
<div class="form responsive-width-100">
<label for="code"><i class="required">*</i> Code</label>
<input id="code" type="text" name="discount_code" placeholder="Code" value="<?=$discount['discount_code']?>" required>
<label for="categories">Categories</label>
<div class="multiselect" data-name="categories[]">
<?php foreach ($discount['categories'] as $cat): ?>
<span class="item" data-value="<?=$cat['id']?>">
<i class="remove">&times;</i><?=$cat['name']?>
<input type="hidden" name="categories[]" value="<?=$cat['id']?>">
</span>
<?php endforeach; ?>
<input type="text" class="search" id="categories" placeholder="Categories">
<div class="list">
<?php foreach ($categories as $cat): ?>
<span data-value="<?=$cat['id']?>"><?=$cat['name']?></span>
<?php endforeach; ?>
</div>
</div>
<label for="products">Products</label>
<div class="multiselect" data-name="products[]">
<?php foreach ($discount['products'] as $product): ?>
<span class="item" data-value="<?=$product['id']?>">
<i class="remove">&times;</i><?=$product['name']?>
<input type="hidden" name="products[]" value="<?=$product['id']?>">
</span>
<?php endforeach; ?>
<input type="text" class="search" id="products" placeholder="Products">
<div class="list">
<?php foreach ($products as $product): ?>
<span data-value="<?=$product['id']?>"><?=$product['name']?></span>
<?php endforeach; ?>
</div>
</div>
<label for="type"><i class="required">*</i> Type</label>
<select id="type" name="discount_type">
<?php foreach ($types as $type): ?>
<option value="<?=$type?>"<?=$discount['discount_type']==$type?' selected':''?>><?=$type?></option>
<?php endforeach; ?>
</select>
<label for="discount_value"><i class="required">*</i> Value</label>
<input id="discount_value" type="number" name="discount_value" placeholder="Value" min="0" step=".01" value="<?=$discount['discount_value']?>" required>
<label for="start_date"><i class="required">*</i> Start Date</label>
<input id="start_date" type="datetime-local" name="start_date" placeholder="Start Date" value="<?=date('Y-m-d\TH:i', strtotime($discount['start_date']))?>" required>
<label for="end_date"><i class="required">*</i> End Date</label>
<input id="end_date" type="datetime-local" name="end_date" placeholder="End Date" value="<?=date('Y-m-d\TH:i', strtotime($discount['end_date']))?>" required>
</div>
</div>
</form>
<?=template_admin_footer()?>

89
admin/discounts.php Normal file
View File

@@ -0,0 +1,89 @@
<?php
defined('admin') or exit;
// SQL query to get all discounts from the "discounts" table
$stmt = $pdo->prepare('SELECT d.*, GROUP_CONCAT(DISTINCT p.name) product_names, GROUP_CONCAT(DISTINCT c.name) category_names FROM discounts d LEFT JOIN products p ON FIND_IN_SET(p.id, d.product_ids) LEFT JOIN categories c ON FIND_IN_SET(c.id, d.category_ids) GROUP BY d.id, d.category_ids, d.product_ids, d.discount_code, d.discount_type, d.discount_type, d.discount_value, d.start_date, d.end_date');
$stmt->execute();
$discounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the current date
$current_date = strtotime((new DateTime())->format('Y-m-d H:i:s'));
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Discount created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Discount updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Discount deleted successfully!';
}
}
?>
<?=template_admin_header('Discounts', 'discounts')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-tag"></i>
<div class="txt">
<h2>Discounts</h2>
<p>View, create, and edit discounts.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=discount" class="btn">Create Discount</a>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td class="responsive-hidden">#</td>
<td>Code</td>
<td>Active</td>
<td class="responsive-hidden">Categories</td>
<td class="responsive-hidden">Products</td>
<td>Type</td>
<td>Value</td>
<td class="responsive-hidden">Start Date</td>
<td class="responsive-hidden">End Date</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($discounts)): ?>
<tr>
<td colspan="8" style="text-align:center;">There are no discounts</td>
</tr>
<?php else: ?>
<?php foreach ($discounts as $discount): ?>
<tr>
<td class="responsive-hidden"><?=$discount['id']?></td>
<td><?=$discount['discount_code']?></td>
<td><?=$current_date >= strtotime($discount['start_date']) && $current_date <= strtotime($discount['end_date']) ? 'Yes' : 'No'?></td>
<td class="responsive-hidden"><?=$discount['category_names'] ? str_replace(',', ', ', $discount['category_names']) : 'all'?></td>
<td class="responsive-hidden"><?=$discount['product_names'] ? str_replace(',', ', ', $discount['product_names']) : 'all'?></td>
<td><?=$discount['discount_type']?></td>
<td><?=$discount['discount_value']?></td>
<td class="responsive-hidden"><?=date('Y-m-d h:ia', strtotime($discount['start_date']))?></td>
<td class="responsive-hidden"><?=date('Y-m-d h:ia', strtotime($discount['end_date']))?></td>
<td><a href="index.php?page=discount&id=<?=$discount['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?=template_admin_footer()?>

62
admin/emailtemplates.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
defined('admin') or exit;
// Capture post data
if (isset($_POST['emailtemplate'], $_POST['emailtemplate2'])) {
// Save templates
file_put_contents('../order-details-template.php', $_POST['emailtemplate']);
file_put_contents('../order-notification-template.php', $_POST['emailtemplate2']);
header('Location: index.php?page=emailtemplates&success_msg=1');
exit;
}
// Read the order details template PHP file
$contents = file_get_contents('../order-details-template.php');
// Read the order notification template PHP file
$contents2 = file_get_contents('../order-notification-template.php');
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Settings updated successfully!';
}
}
?>
<?=template_admin_header('Email Templates', 'emailtemplates')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Email Templates</h2>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="tabs">
<a href="#" class="active">Order Details Template</a>
<a href="#" class="">Order Notification Template</a>
</div>
<div class="content-block">
<div class="form responsive-width-100">
<div class="tab-content active">
<label for="emailtemplate">Order Details Template:</label>
<textarea name="emailtemplate" id="emailtemplate" style="min-height: 100vh;"><?=$contents?></textarea>
</div>
<div class="tab-content">
<label for="emailtemplate2">Order Notification Template:</label>
<textarea name="emailtemplate2" id="emailtemplate2" style="min-height: 100vh;"><?=$contents2?></textarea>
</div>
</div>
</div>
</form>
<script>
document.querySelectorAll("input[type='checkbox']").forEach(checkbox => {
checkbox.onclick = () => checkbox.value = checkbox.checked ? 'true' : 'false';
});
</script>
<?=template_admin_footer()?>

60
admin/factuur.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
(defined(security_key) or defined('admin') or defined('interface')) or exit;
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
$order_number = $_POST['txn_id'];
list($data,$customer_email,$order_id) = generateInvoice($pdo,$order_number);
$dompdf->loadHtml($data);
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'portrait');
// Render the HTML as PDF
$dompdf->render();
$file_name = 'Factuur - '.$order_id;
// Output the generated PDF to Browser
if (isset($_POST['email_invoice']) || $_POST['status'] == 'Paid'){
$to = $customer_email;
$subject = 'Factuur - '.$order_id;
$message = $data;
$attachment = $dompdf->output();
$attachment_name = $file_name;
$header_redirect = 'Location: index.php?page=order&id='.$order_id;
//Send to PHPMailer
send_mail_by_PHPMailer($to, $subject, $message, $attachment, $attachment_name);
header($header_redirect);
exit;
}
if (isset($_POST['email_invoice_to_admin']) || $_POST['status'] == 'Paid'){
$to = $customer_email;
$subject = 'Factuur - '.$order_id;
$message = $data;
$attachment = $dompdf->output();
$attachment_name = $file_name;
$header_redirect = 'Location: index.php?page=order&id='.$order_id;
//Send to PHPMailer
if(invoice_bookkeeping){
send_mail_by_PHPMailer(email_bookkeeping, $subject, $data, $attachment, $subject);
}
header($header_redirect);
exit;
}
if (isset($_POST['show_invoice'])){
ob_end_clean();
$dompdf->stream("Factuur.pdf", array("Attachment" => false));
exit;
}
?>

42
admin/index.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
define('admin', true);
//error reporting
// Determine the base URL
$base_url = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] === 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? 'https' : 'http';
$base_url .= '://' . rtrim($_SERVER['HTTP_HOST'], '/');
$base_url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 || strpos($_SERVER['HTTP_HOST'], ':') !== false ? '' : ':' . $_SERVER['SERVER_PORT'];
$base_url .= '/' . ltrim(substr(str_replace('\\', '/', realpath(__DIR__)), strlen($_SERVER['DOCUMENT_ROOT'])), '/');
define('base_url', rtrim($base_url, '/') . '/');
session_start();
// Include the configuration file, this contains settings you can change.
include '../config.php';
// Include functions and connect to the database using PDO MySQL
include '../functions.php';
// Connect to MySQL database
$pdo = pdo_connect_mysql();
// If the user is not logged-in redirect them to the login page
if (!isset($_SESSION['account_loggedin'])) {
header('Location: ' . url('../index.php?page=myaccount'));
exit;
}
// If the user is not admin redirect them back to the shopping cart home page
$stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ?');
$stmt->execute([ $_SESSION['account_id'] ]);
$account = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$account || $account['role'] != 'Admin') {
header('Location: ' . url('../index.php'));
exit;
}
// Page is set to home (home.php) by default, so when the visitor visits that will be the page they see.
$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'dashboard';
if (isset($_GET['page']) && $_GET['page'] == 'logout') {
session_destroy();
header('Location: ' . url('../index.php'));
exit;
}
// Output error variable
$error = '';
// Include the requested page
include $page . '.php';
?>

66
admin/language.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
defined('admin') or exit;
// Capture post data
if (isset($_POST['language_US'], $_POST['language_NL'])) {
// Save templates
file_put_contents('../custom/translations_US.php', $_POST['language_US']);
file_put_contents('../custom/translations_NL.php', $_POST['language_NL']);
header('Location: index.php?page=language&success_msg=1');
exit;
}
// Read language_US template PHP file
$contents = file_get_contents('../custom/translations_US.php');
// Read language template PHP file
$contents2 = file_get_contents('../custom/translations_NL.php');
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Settings updated successfully!';
}
}
?>
<?=template_admin_header('Language', 'language')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Translations</h2>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="tabs">
<a href="#" class="active">US</a>
<a href="#" class="">NL</a>
</div>
<div class="content-block">
<div class="form responsive-width-100">
<div class="tab-content active">
<label for="language_US">Language_US:</label>
<textarea name="language_US" id="language_US" style="min-height: 100vh;"><?=$contents?></textarea>
</div>
<div class="tab-content">
<label for="language_NL">Language_NL:</label>
<textarea name="language_NL" id="language_NL" style="min-height: 100vh;"><?=$contents2?></textarea>
</div>
</div>
</div>
</form>
<script>
document.querySelectorAll("input[type='checkbox']").forEach(checkbox => {
checkbox.onclick = () => checkbox.value = checkbox.checked ? 'true' : 'false';
});
</script>
<?=template_admin_footer()?>

99
admin/media.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
defined('admin') or exit;
// Retrieve the GET request parameters (if specified)
$pagination_page = isset($_GET['pagination_page']) ? $_GET['pagination_page'] : 1;
$search = isset($_GET['search']) ? $_GET['search'] : '';
// Order by column
$order = isset($_GET['order']) && $_GET['order'] == 'DESC' ? 'DESC' : 'ASC';
// Add/remove columns to the whitelist array
$order_by_whitelist = ['id','title','caption','date_uploaded'];
$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], $order_by_whitelist) ? $_GET['order_by'] : 'id';
// Number of results per pagination page
$results_per_page = 20;
// Declare query param variables
$param1 = ($pagination_page - 1) * $results_per_page;
$param2 = $results_per_page;
$param3 = '%' . $search . '%';
// SQL where clause
$where = '';
$where .= $search ? 'WHERE (title LIKE :search OR caption LIKE :search OR full_path LIKE :search) ' : '';
// Retrieve the total number of media
$stmt = $pdo->prepare('SELECT COUNT(*) AS total FROM media ' . $where);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
$media_total = $stmt->fetchColumn();
// SQL query to get all media from the "media" table
$stmt = $pdo->prepare('SELECT * FROM media ' . $where . ' ORDER BY ' . $order_by . ' ' . $order . ' LIMIT :start_results,:num_results');
// Bind params
$stmt->bindParam('start_results', $param1, PDO::PARAM_INT);
$stmt->bindParam('num_results', $param2, PDO::PARAM_INT);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
// Retrieve query results
$media = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Determine the URL
$url = 'index.php?page=media&search=' . $search;
?>
<?=template_admin_header('Media', 'media')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-images"></i>
<div class="txt">
<h2>Media</h2>
<p>View, manage, and search media files.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="#" class="btn upload">Upload</a>
<form action="" method="get">
<input type="hidden" name="page" value="media">
<a href="<?=$url?>&order_by=<?=$order_by?>&order=<?=$order=='ASC'?'DESC':'ASC'?>"><i class="fa-solid fa-arrow-<?=$order=='ASC'?'up':'down'?>-wide-short"></i></a>
<select name="order_by" onchange="this.form.submit()">
<option value="" disabled>Order By</option>
<option value="id"<?=$order_by=='id'?' selected':''?>>ID</option>
<option value="title"<?=$order_by=='title'?' selected':''?>>Title</option>
<option value="caption"<?=$order_by=='caption'?' selected':''?>>Caption</option>
<option value="date_uploaded"<?=$order_by=='date_uploaded'?' selected':''?>>Date Uploaded</option>
</select>
<div class="search">
<label for="search">
<input id="search" type="text" name="search" placeholder="Search media..." value="<?=htmlspecialchars($search, ENT_QUOTES)?>" class="responsive-width-100">
<i class="fas fa-search"></i>
</label>
</div>
</form>
</div>
<div class="content-block media-page">
<div class="media">
<?php foreach ($media as $m): ?>
<a href="#" class="image" data-id="<?=$m['id']?>" data-full-path="<?=$m['full_path']?>" data-title="<?=$m['title']?>" data-caption="<?=$m['caption']?>" data-date-uploaded="<?=date('Y-m-d\TH:i', strtotime($m['date_uploaded']))?>">
<img src="../<?=$m['full_path']?>" alt="<?=$m['caption']?>" loading="lazy">
</a>
<?php endforeach; ?>
</div>
</div>
<div class="pagination">
<?php if ($pagination_page > 1): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page-1?>&order=<?=$order?>&order_by=<?=$order_by?>">Prev</a>
<?php endif; ?>
<span>Page <?=$pagination_page?> of <?=ceil($media_total / $results_per_page) == 0 ? 1 : ceil($media_total / $results_per_page)?></span>
<?php if ($pagination_page * $results_per_page < $media_total): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page+1?>&order=<?=$order?>&order_by=<?=$order_by?>">Next</a>
<?php endif; ?>
</div>
<?=template_admin_footer('initMedia()')?>

290
admin/order.php Normal file
View File

@@ -0,0 +1,290 @@
<?php
defined('admin') or exit;
if (!isset($_GET['id'])) {
exit('Invalid ID!');
}
// Retrieve order items
$stmt = $pdo->prepare('SELECT ti.*, p.productcode, p.name FROM transactions t JOIN transactions_items ti ON ti.txn_id = t.txn_id LEFT JOIN products p ON p.id = ti.item_id WHERE t.id = ?');
$stmt->execute([ $_GET['id'] ]);
$order_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Retrieve order details
$stmt = $pdo->prepare('SELECT a.email, a.id AS a_id, a.first_name AS a_first_name, a.last_name AS a_last_name, a.address_street AS a_address_street, a.address_city AS a_address_city, a.address_state AS a_address_state, a.address_zip AS a_address_zip, a.address_country AS a_address_country, a.address_phone AS a_address_phone, t.* FROM transactions t LEFT JOIN transactions_items ti ON ti.txn_id = t.txn_id LEFT JOIN accounts a ON a.id = t.account_id WHERE t.id = ?');
$stmt->execute([ $_GET['id'] ]);
$order = $stmt->fetch(PDO::FETCH_ASSOC);
// Get tax
$stmt = $pdo->prepare('SELECT * FROM taxes WHERE country = ?');
$stmt->execute([$order['a_address_country']]);
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
$tax_rate = $tax ? $tax['rate'] : 0.00;
//Add giftcards
if (isset($_GET['add_giftcard'])){
createGiftCart($pdo, $order['txn_id']);
}
//Get connected giftcards
$giftcards_template = $order['txn_id'].'#%#%';
$stmt = $pdo->prepare('SELECT * from discounts WHERE discount_code like ?');
$stmt->execute([$giftcards_template]);
$giftcards = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the current date
$current_date = strtotime((new DateTime())->format('Y-m-d H:i:s'));
// Delete transaction
if (isset($_GET['delete'])) {
// Delete the transaction
$stmt = $pdo->prepare('DELETE t, ti FROM transactions t LEFT JOIN transactions_items ti ON ti.txn_id = t.txn_id WHERE t.id = ?');
$stmt->execute([ $_GET['id'] ]);
// Deactive giftcards
removeGiftCart($pdo, $_GET['txn']);
header('Location: index.php?page=orders&success_msg=3');
exit;
}
if (!$order) {
exit('Invalid ID!');
}
?>
<?=template_admin_header('Orders', 'orders')?>
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Order #<?=$_GET['id']?></h2>
<a href="index.php?page=orders" class="btn alt mar-right-2">Cancel</a>
<a href="index.php?page=order&id=<?=$_GET['id']?>&delete=true&txn=<?=$order['txn_id']?>" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this order?')">Delete</a>
<a href="index.php?page=order_manage&id=<?=$_GET['id']?>" class="btn">Edit</a>
</div>
<div class="content-block-wrapper">
<div class="content-block order-details">
<div class="block-header">
<i class="fa-solid fa-cart-shopping fa-sm"></i>Order Details
</div>
<div class="order-detail">
<h3>Order ID</h3>
<p><?=$order['id']?></p>
</div>
<div class="order-detail">
<h3>Transaction ID</h3>
<p><?=$order['txn_id']?></p>
</div>
<?php if ($order['shipping_method']): ?>
<div class="order-detail">
<h3>Shipping Method</h3>
<p><?=$order['shipping_method'] ? htmlspecialchars($order['shipping_method'], ENT_QUOTES) : '--'?></p>
</div>
<?php endif; ?>
<div class="order-detail">
<h3>Payment Method</h3>
<p><?=$order['payment_method']?></p>
</div>
<div class="order-detail">
<h3>Payment Status</h3>
<p><?=$order['payment_status']?></p>
</div>
<div class="order-detail">
<h3>Date</h3>
<p><?=date('F j, Y H:ia', strtotime($order['created']))?></p>
</div>
<?php if ($order['discount_code']): ?>
<div class="order-detail">
<h3>Discount Code</h3>
<p><?=htmlspecialchars($order['discount_code'], ENT_QUOTES)?></p>
</div>
<?php endif; ?>
</div>
<div class="content-block order-details">
<div class="block-header">
<i class="fa-solid fa-user fa-sm"></i>Account Details
</div>
<?php if ($order['email']): ?>
<div class="order-detail">
<h3>Email</h3>
<p><a href="index.php?page=account&id=<?=$order['a_id']?>" target="_blank" class="link1" style="margin:0"><?=htmlspecialchars($order['email'], ENT_QUOTES)?></a></p>
</div>
<div class="order-detail">
<h3>Name</h3>
<p><?=htmlspecialchars($order['a_first_name'], ENT_QUOTES)?> <?=htmlspecialchars($order['a_last_name'], ENT_QUOTES)?></p>
</div>
<div class="order-detail">
<h3>Address</h3>
<p style="text-align:right;"><?=htmlspecialchars($order['a_address_street'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['a_address_city'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['a_address_state'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['a_address_zip'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['a_address_country'], ENT_QUOTES)?>
</p>
</div>
<div class="order-detail">
<h3>Contact</h3>
<p style="text-align:right;"><?=htmlspecialchars($order['a_address_phone'], ENT_QUOTES)?>
</p>
</div>
<?php else: ?>
<p>The order is not associated with an account.</p>
<?php endif; ?>
</div>
<div class="content-block order-details">
<div class="block-header">
<i class="fa-solid fa-user fa-sm"></i>Customer Details
</div>
<div class="order-detail">
<h3>Email</h3>
<p><?=htmlspecialchars($order['payer_email'], ENT_QUOTES)?></p>
</div>
<div class="order-detail">
<h3>Name</h3>
<p><?=htmlspecialchars($order['first_name'], ENT_QUOTES)?> <?=htmlspecialchars($order['last_name'], ENT_QUOTES)?></p>
</div>
<div class="order-detail">
<h3>Address</h3>
<p style="text-align:right;"><?=htmlspecialchars($order['address_street'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['address_city'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['address_state'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['address_zip'], ENT_QUOTES)?><br>
<?=htmlspecialchars($order['address_country'], ENT_QUOTES)?>
</p>
</div>
<div class="order-detail">
<h3>Contact</h3>
<p style="text-align:right;"><?=htmlspecialchars($order['a_address_phone'], ENT_QUOTES)?>
</p>
</div>
</div>
</div>
<div class="content-block">
<div class="block-header">
<i class="fa-solid fa-bars fa-sm"></i>Order
</div>
<div class="table order-table">
<table>
<thead>
<tr>
<td>Product</td>
<td>Options</td>
<td>Qty</td>
<td class="responsive-hidden">Price</td>
<td style="text-align:right;">Total</td>
</tr>
</thead>
<tbody>
<?php if (empty($order_items)): ?>
<tr>
<td colspan="5" style="text-align:center;">There are no order items</td>
</tr>
<?php else: ?>
<?php
$subtotal = 0;
foreach ($order_items as $item):
?>
<tr>
<td><?=$item['productcode']?> <?=$item['name'] ? htmlspecialchars($item['name'], ENT_QUOTES) : '(Product ' . $item['item_id'] . ')'?></td>
<td><?=$item['item_options'] ? htmlspecialchars(str_replace(',', ', ', $item['item_options']), ENT_QUOTES) : '--'?></td>
<td><?=$item['item_quantity']?></td>
<td class="responsive-hidden"><?=currency_code?><?=number_format($item['item_price'], 2)?></td>
<td style="text-align:right;"><?=currency_code?><?=number_format($item['item_price']*$item['item_quantity'], 2)?></td>
</tr>
<?php $subtotal += $item['item_price']*$item['item_quantity'];?>
<?php endforeach; ?>
<?php endif; ?>
<tr>
<td colspan="5" class="item-list-end"></td>
</tr>
<tr>
<td colspan="4" class="subtotal">Subtotal</td>
<td class="num"><?=currency_code?><?=number_format($subtotal, 2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping">Shipping</td>
<td class="num"><?=currency_code?><?=number_format($order['shipping_amount'], 2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping">Discount</td>
<td class="num"><?=currency_code?><?=number_format(($order['payment_amount']+$order['shipping_amount'])-($subtotal), 2)?></td>
</tr>
<tr>
<td colspan="4" class="shipping">VAT</td>
<td class="num" style="border-bottom: 1px solid #f0f1f2;"><?=currency_code?><?=number_format($order['tax_amount'], 2)?></td>
</tr>
<tr>
<td colspan="4" class="total">Total</td>
<td class="num"><b><?=currency_code?><?=number_format($order['payment_amount'], 2)?></b></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="content-block">
<div class="block-header">
<i class="fa-solid fa-bars fa-sm"></i>Giftcards
</div>
<div class="table order-table">
<a href="index.php?page=order&id=<?=$_GET['id']?>&add_giftcard" class="btn">Relate giftcards</a>
<table>
<thead>
<tr>
<td>Giftcard</td>
<td>Valid</td>
<td>Value</td>
</tr>
</thead>
<tbody>
<?php if (empty($giftcards)): ?>
<tr>
<td colspan="5" style="text-align:center;">There are no order items</td>
</tr>
<?php else: ?>
<?php foreach ($giftcards as $giftcard): ?>
<tr>
<td><?=$giftcard['discount_code']?></td>
<td><?=$current_date >= strtotime($giftcard['start_date']) && $current_date <= strtotime($giftcard['end_date']) ? 'Yes' : 'No'?></td>
<td><?=currency_code?><?=number_format($giftcard['discount_value'], 2)?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<tr>
<td colspan="5" class="item-list-end"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="content-block">
<div class="block-header">
<i class="fa-solid fa-bars fa-sm"></i>Invoice
</div>
<div class="table order-table">
<table>
<tr>
<td style="width:70px";>
<form action="index.php?page=factuur" method="post">
<input type="hidden" name="txn_id" value="<?=$order['txn_id']?>">
<input type="submit" class="btn" name="show_invoice" value="Show">
</form>
</td>
<td style="width: 157px;">
<form action="index.php?page=factuur" method="post">
<input type="hidden" name="txn_id" value="<?=$order['txn_id']?>">
<input type="submit" class="btn" name="email_invoice" value="Email to Customer" onclick="return confirm('Send invoice to customer?');">
</form>
</td>
<td>
<form action="index.php?page=factuur" method="post">
<input type="hidden" name="txn_id" value="<?=$order['txn_id']?>">
<input type="submit" class="btn" name="email_invoice_to_admin" value="Email to Admin" onclick="return confirm('Send invoice to admin?');">
</form>
</td>
</tr>
</table>
</div>
</div>
<?=template_admin_footer()?>

261
admin/order_manage.php Normal file
View File

@@ -0,0 +1,261 @@
<?php
defined('admin') or exit;
// Default transaction values
$transaction = [
'txn_id' => '',
'payment_amount' => '',
'payment_status' => '',
'payer_email' => '',
'first_name' => '',
'last_name' => '',
'account_id' => '',
'payment_method' => '',
'discount_code' => '',
'address_street' => '',
'address_city' => '',
'address_state' => '',
'address_zip' => '',
'address_country' => '',
'shipping_method' => '',
'shipping_amount' => '',
'created' => date('Y-m-d\TH:i'),
'address_phone' => ''
];
// Retrieve the products from the database
$stmt = $pdo->prepare('SELECT * FROM products ORDER BY id');
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Retrieve the accounts from the database
$stmt = $pdo->prepare('SELECT * FROM accounts ORDER BY id');
$stmt->execute();
$accounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Add transactions items to the database
function addOrderItems($pdo, $txn_id) {
if (isset($_POST['item_id']) && is_array($_POST['item_id']) && count($_POST['item_id']) > 0) {
// Iterate items
$delete_list = [];
for ($i = 0; $i < count($_POST['item_id']); $i++) {
// If the item doesnt exist in the database
if (!intval($_POST['item_id'][$i])) {
// Insert new item
$stmt = $pdo->prepare('INSERT INTO transactions_items (txn_id,item_id,item_price,item_quantity,item_options) VALUES (?,?,?,?,?)');
$stmt->execute([ $txn_id, $_POST['item_product'][$i], $_POST['item_price'][$i], $_POST['item_quantity'][$i], $_POST['item_options'][$i] ]);
$delete_list[] = $pdo->lastInsertId();
} else {
// Update existing item
$stmt = $pdo->prepare('UPDATE transactions_items SET txn_id = ?, item_id = ?, item_price = ?, item_quantity = ?, item_options = ? WHERE id = ?');
$stmt->execute([ $txn_id, $_POST['item_product'][$i], $_POST['item_price'][$i], $_POST['item_quantity'][$i], $_POST['item_options'][$i], $_POST['item_id'][$i] ]);
$delete_list[] = $_POST['item_id'][$i];
}
}
// Delete item
$in = str_repeat('?,', count($delete_list) - 1) . '?';
$stmt = $pdo->prepare('DELETE FROM transactions_items WHERE txn_id = ? AND id NOT IN (' . $in . ')');
$stmt->execute(array_merge([ $txn_id ], $delete_list));
} else {
// No item exists, delete all
$stmt = $pdo->prepare('DELETE FROM transactions_items WHERE txn_id = ?');
$stmt->execute([ $txn_id ]);
}
}
// Save captured data
if (isset($_GET['id'])) {
// Retrieve the transaction from the database
$stmt = $pdo->prepare('SELECT * FROM transactions WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$transaction = $stmt->fetch(PDO::FETCH_ASSOC);
// Retrieve the transaction items from the database
$stmt = $pdo->prepare('SELECT * FROM transactions_items WHERE txn_id = ?');
$stmt->execute([ $transaction['txn_id'] ]);
$transactions_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ID param exists, edit an existing transaction
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the transaction
$stmt = $pdo->prepare('UPDATE transactions SET txn_id = ?, payment_amount = ?, payment_status = ?, created = ?, payer_email = ?, first_name = ?, last_name = ?, address_street = ?, address_city = ?, address_state = ?, address_zip = ?, address_country = ?, account_id = ?, payment_method = ?, discount_code = ?, shipping_method = ?, shipping_amount = ?, address_phone= ? WHERE id = ?');
$stmt->execute([ $_POST['txn_id'], $_POST['amount'], $_POST['status'], date('Y-m-d H:i:s', strtotime($_POST['created'])), $_POST['email'], $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], empty($_POST['account']) ? NULL : $_POST['account'], $_POST['method'], $_POST['discount_code'], $_POST['shipping_method'], $_POST['shipping_amount'], $_POST['address_phone'], $_GET['id'] ]);
addOrderItems($pdo, $_POST['txn_id']);
if ($_POST['status'] == 'Paid'){
createGiftCart($pdo, $_POST['txn_id']);
include_once('./factuur.php');
}
header('Location: index.php?page=orders&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the transaction
$stmt = $pdo->prepare('DELETE t, ti FROM transactions t LEFT JOIN transactions_items ti ON ti.txn_id = t.txn_id WHERE t.id = ?');
$stmt->execute([ $_GET['id'] ]);
// Deactive giftcards
removeGiftCart($pdo, $_POST['txn_id']);
header('Location: index.php?page=orders&success_msg=3');
exit;
}
} else {
// Create a new transaction
$page = 'Create';
if (isset($_POST['submit'])) {
$stmt = $pdo->prepare('INSERT INTO transactions (txn_id,payment_amount,payment_status,created,payer_email,first_name,last_name,address_street,address_city,address_state,address_zip,address_country,account_id,payment_method,discount_code,shipping_method,shipping_amount, address_phone) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([ $_POST['txn_id'], $_POST['amount'], $_POST['status'], date('Y-m-d H:i:s', strtotime($_POST['created'])), $_POST['email'], $_POST['first_name'], $_POST['last_name'], $_POST['address_street'], $_POST['address_city'], $_POST['address_state'], $_POST['address_zip'], $_POST['address_country'], empty($_POST['account']) ? NULL : $_POST['account'], $_POST['method'], $_POST['discount_code'], $_POST['shipping_method'], $_POST['shipping_amount'], $_POST['address_phone'] ]);
addOrderItems($pdo, $_POST['txn_id']);
header('Location: index.php?page=orders&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Order', 'orders', 'manage')?>
<form action="" method="post">
<div class="content-title">
<h2><?=$page?> Order</h2>
<a href="index.php?page=orders" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this order?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="tabs">
<a href="#" class="active">Details</a>
<a href="#">Address</a>
<a href="#">Items</a>
</div>
<div class="content-block tab-content active">
<div class="form responsive-width-100">
<label for="txn_id"><i class="required">*</i> Transaction ID</label>
<input id="txn_id" type="text" name="txn_id" placeholder="Transaction ID" value="<?=$transaction['txn_id']?>" required>
<label for="status"><i class="required">*</i> Status</label>
<select id="status" name="status" required>
<option value="New"<?=$transaction['payment_status']=='New'?' selected':''?>>New</option>
<option value="Paid"<?=$transaction['payment_status']=='Paid'?' selected':''?>>Paid</option>
<option value="Pending"<?=$transaction['payment_status']=='Pending'?' selected':''?>>Pending</option>
<option value="Completed"<?=$transaction['payment_status']=='Completed'?' selected':''?>>Completed</option>
<option value="Cancelled"<?=$transaction['payment_status']=='Cancelled'?' selected':''?>>Cancelled</option>
<option value="Failed"<?=$transaction['payment_status']=='Failed'?' selected':''?>>Failed</option>
<option value="Reversed"<?=$transaction['payment_status']=='Reversed'?' selected':''?>>Reversed</option>
<option value="Refunded"<?=$transaction['payment_status']=='Refunded'?' selected':''?>>Refunded</option>
<option value="Shipped"<?=$transaction['payment_status']=='Shipped'?' selected':''?>>Shipped</option>
</select>
<label for="amount"><i class="required">*</i> Payment Amount</label>
<input id="amount" type="number" name="amount" placeholder="0.00" value="<?=$transaction['payment_amount']?>" step=".01" required>
<label for="email"><i class="required">*</i> Customer Email</label>
<input id="email" type="email" name="email" placeholder="joebloggs@example.com" value="<?=htmlspecialchars($transaction['payer_email'], ENT_QUOTES)?>" required>
<label for="account">Account</label>
<select id="account" name="account">
<option value=""<?=$transaction['account_id']==NULL?' selected':''?>>(none)</option>
<?php foreach ($accounts as $account): ?>
<option value="<?=$account['id']?>"<?=$account['id']==$transaction['account_id']?' selected':''?>><?=$account['id']?> - <?=htmlspecialchars($account['email'], ENT_QUOTES)?></option>
<?php endforeach; ?>
</select>
<label for="first_name">First Name</label>
<input id="first_name" type="text" name="first_name" placeholder="Joe" value="<?=htmlspecialchars($transaction['first_name'], ENT_QUOTES)?>">
<label for="last_name">Last Name</label>
<input id="last_name" type="text" name="last_name" placeholder="Bloggs" value="<?=htmlspecialchars($transaction['last_name'], ENT_QUOTES)?>">
<label for="method">Payment Method</label>
<input id="method" type="text" name="method" placeholder="website" value="<?=$transaction['payment_method']?>">
<label for="shipping_method">Shipping Method</label>
<input id="shipping_method" type="text" name="shipping_method" placeholder="Standard" value="<?=$transaction['shipping_method']?>">
<label for="shipping_amount"><i class="required">*</i> Shipping Amount</label>
<input id="shipping_amount" type="number" name="shipping_amount" placeholder="0.00" value="<?=$transaction['shipping_amount']?>" step=".01" required>
<label for="discount_code">Discount Code</label>
<input id="discount_code" type="text" name="discount_code" placeholder="Discount Code" value="<?=htmlspecialchars($transaction['discount_code'], ENT_QUOTES)?>">
<label for="created"><i class="required">*</i> Date</label>
<input id="created" type="datetime-local" name="created" value="<?=date('Y-m-d\TH:i', strtotime($transaction['created']))?>" required>
</div>
</div>
<div class="content-block tab-content">
<div class="form responsive-width-100">
<label for="address_street">Address Street</label>
<input id="address_street" type="text" name="address_street" placeholder="" value="<?=htmlspecialchars($transaction['address_street'], ENT_QUOTES)?>">
<label for="address_city">Address City</label>
<input id="address_city" type="text" name="address_city" placeholder="" value="<?=htmlspecialchars($transaction['address_city'], ENT_QUOTES)?>">
<label for="address_state">Address State</label>
<input id="address_state" type="text" name="address_state" placeholder="" value="<?=htmlspecialchars($transaction['address_state'], ENT_QUOTES)?>">
<label for="address_zip">Address Zip</label>
<input id="address_zip" type="text" name="address_zip" placeholder="" value="<?=htmlspecialchars($transaction['address_zip'], ENT_QUOTES)?>">
<label for="address_phone">Phone</label>
<input id="address_phone" type="text" name="address_phone" placeholder="" value="<?=htmlspecialchars($transaction['address_phone'], ENT_QUOTES)?>">
<label for="address_country">Country</label>
<select id="address_country" name="address_country" required>
<?php foreach(get_countries() as $country): ?>
<option value="<?=$country?>"<?=$country==$transaction['address_country']?' selected':''?>><?=$country?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="content-block tab-content">
<div class="table manage-order-table">
<table>
<thead>
<tr>
<td>Product</td>
<td>Price</td>
<td>Quantity</td>
<td>Options</td>
<td></td>
</tr>
</thead>
<tbody>
<?php if (empty($transactions_items)): ?>
<tr>
<td colspan="5" style="text-align:center;" class="no-order-items-msg">There are no order items</td>
</tr>
<?php else: ?>
<?php foreach ($transactions_items as $item): ?>
<tr>
<td>
<input type="hidden" name="item_id[]" value="<?=$item['id']?>">
<select name="item_product[]">
<?php foreach ($products as $product): ?>
<option value="<?=$product['id']?>"<?=$item['item_id']==$product['id']?' selected':''?>><?=$product['id']?> - <?=htmlspecialchars($product['name'], ENT_QUOTES)?></option>
<?php endforeach; ?>
</select>
</td>
<td><input name="item_price[]" type="number" placeholder="Price" value="<?=$item['item_price']?>" step=".01"></td>
<td><input name="item_quantity[]" type="number" placeholder="Quantity" value="<?=$item['item_quantity']?>"></td>
<td><input name="item_options[]" type="text" placeholder="Options" value="<?=htmlspecialchars($item['item_options'], ENT_QUOTES)?>"></td>
<td><i class="fa-solid fa-xmark delete-item"></i></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<a href="#" class="add-item"><i class="fa-solid fa-plus"></i>Add Item</a>
</div>
</div>
</form>
<?=template_admin_footer('initManageOrder(' . json_encode($products) . ')')?>

172
admin/orders.php Normal file
View File

@@ -0,0 +1,172 @@
<?php
defined('admin') or exit;
// Retrieve the GET request parameters (if specified)
$pagination_page = isset($_GET['pagination_page']) ? $_GET['pagination_page'] : 1;
$search = isset($_GET['search']) ? $_GET['search'] : '';
// Filters parameters
$status = isset($_GET['status']) ? $_GET['status'] : '';
$method = isset($_GET['method']) ? $_GET['method'] : '';
$account_id = isset($_GET['account_id']) ? $_GET['account_id'] : '';
// Order by column
$order = isset($_GET['order']) && $_GET['order'] == 'ASC' ? 'ASC' : 'DESC';
// Add/remove columns to the whitelist array
$order_by_whitelist = ['id','first_name','total_products','payment_amount','payment_method','payment_status','created','payer_email'];
$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], $order_by_whitelist) ? $_GET['order_by'] : 'created';
// Number of results per pagination page
$results_per_page = 20;
// Declare query param variables
$param1 = ($pagination_page - 1) * $results_per_page;
$param2 = $results_per_page;
$param3 = '%' . $search . '%';
// SQL where clause
$where = '';
$where .= $search ? 'WHERE (t.first_name LIKE :search OR t.last_name LIKE :search OR t.id LIKE :search OR t.txn_id LIKE :search OR t.payer_email LIKE :search) ' : '';
// Add filters
// Payment status filter
if ($status == 1) $where .= $where ? 'AND payment_status = "Completed" ' : 'WHERE payment_status = "Completed" ';
if ($status == 2) $where .= $where ? 'AND payment_status = "Pending" ' : 'WHERE payment_status = "Pending" ';
if ($status == 3) $where .= $where ? 'AND payment_status = "Cancelled" ' : 'WHERE payment_status = "Cancelled" ';
if ($status == 4) $where .= $where ? 'AND payment_status = "Reversed" ' : 'WHERE payment_status = "Reversed" ';
if ($status == 5) $where .= $where ? 'AND payment_status = "Shipped" ' : 'WHERE payment_status = "Shipped" ';
// Payment method filter
if ($method == 1) $where .= $where ? 'AND payment_method = "website" ' : 'WHERE payment_status = "website" ';
if ($method == 2) $where .= $where ? 'AND payment_method = "paypal" ' : 'WHERE payment_status = "paypal" ';
if ($method == 3) $where .= $where ? 'AND payment_method = "stripe" ' : 'WHERE payment_status = "stripe" ';
// Account ID filter
if ($account_id) $where .= $where ? 'AND account_id = :account_id ' : 'WHERE account_id = :account_id ';
// Retrieve the total number of transactions
$stmt = $pdo->prepare('SELECT COUNT(DISTINCT t.id) AS total FROM transactions t LEFT JOIN transactions_items ti ON ti.txn_id = t.txn_id ' . $where);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
if ($account_id) $stmt->bindParam('account_id', $account_id, PDO::PARAM_INT);
$stmt->execute();
$orders_total = $stmt->fetchColumn();
// Retrieve transactions
$stmt = $pdo->prepare('SELECT t.*, COUNT(ti.id) AS total_products FROM transactions t LEFT JOIN transactions_items ti ON ti.txn_id = t.txn_id ' . $where . ' GROUP BY t.id, t.txn_id, t.payment_amount, t.payment_status, t.created, t.payer_email, t.first_name, t.last_name, t.address_street, t.address_city, t.address_state, t.address_zip, t.address_country, t.account_id, t.payment_method, t.discount_code, t.shipping_method, t.shipping_amount ORDER BY ' . $order_by . ' ' . $order . ' LIMIT :start_results,:num_results');
// Bind params
$stmt->bindParam('start_results', $param1, PDO::PARAM_INT);
$stmt->bindParam('num_results', $param2, PDO::PARAM_INT);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
if ($account_id) $stmt->bindParam('account_id', $account_id, PDO::PARAM_INT);
$stmt->execute();
// Retrieve query results
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Determine the URL
$url = 'index.php?page=orders&search=' . $search . '&status=' . $status . '&method=' . $method . '&account_id=' . $account_id;
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Order created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Order updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Order deleted successfully!';
}
}
?>
<?=template_admin_header('Orders', 'orders')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-cart-shopping"></i>
<div class="txt">
<h2>Orders</h2>
<p>View, create, and search orders.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=order_manage" class="btn">Create Order</a>
<form action="" method="get">
<input type="hidden" name="page" value="orders">
<div class="filters">
<a href="#"><i class="fas fa-sliders-h"></i> Filters</a>
<div class="list">
<select name="status">
<option value="" disabled selected>Status</option>
<option value="1"<?=$status==1?' selected':''?>>Completed</option>
<option value="2"<?=$status==2?' selected':''?>>Pending</option>
<option value="3"<?=$status==3?' selected':''?>>Cancelled</option>
<option value="4"<?=$status==4?' selected':''?>>Reversed</option>
<option value="5"<?=$status==5?' selected':''?>>Shipped</option>
</select>
<select name="method">
<option value="" disabled selected>Method</option>
<option value="1"<?=$method==1?' selected':''?>>Website</option>
<option value="2"<?=$method==2?' selected':''?>>PayPal</option>
<option value="3"<?=$method==3?' selected':''?>>Stripe</option>
</select>
<button type="submit">Apply</button>
</div>
</div>
<div class="search">
<label for="search">
<input id="search" type="text" name="search" placeholder="Search order..." value="<?=htmlspecialchars($search, ENT_QUOTES)?>" class="responsive-width-100">
<i class="fas fa-search"></i>
</label>
</div>
</form>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=id'?>">#<?php if ($order_by=='id'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=first_name'?>">Customer<?php if ($order_by=='first_name'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=payer_email'?>">Email<?php if ($order_by=='payer_email'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=total_products'?>">Products<?php if ($order_by=='total_products'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=payment_amount'?>">Total<?php if ($order_by=='payment_amount'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=payment_method'?>">Method<?php if ($order_by=='payment_method'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=payment_status'?>">Status<?php if ($order_by=='payment_status'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=created'?>">Date<?php if ($order_by=='created'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($orders)): ?>
<tr>
<td colspan="9" style="text-align:center;">There are no orders</td>
</tr>
<?php else: ?>
<?php foreach ($orders as $i): ?>
<tr>
<td><?=$i['id']?></td>
<td><?=htmlspecialchars($i['first_name'], ENT_QUOTES)?> <?=htmlspecialchars($i['last_name'], ENT_QUOTES)?></td>
<td class="responsive-hidden"><?=htmlspecialchars($i['payer_email'], ENT_QUOTES)?></td>
<td class="responsive-hidden"><?=$i['total_products']?></td>
<td><?=currency_code?><?=number_format($i['payment_amount'], 2)?></td>
<td class="responsive-hidden"><?=$i['payment_method']?></td>
<td class="responsive-hidden"><span class="status <?=strtolower($i['payment_status'])?>"><?=$i['payment_status']?></span></td>
<td class="responsive-hidden"><?=date('F j, Y', strtotime($i['created']))?></td>
<td><a href="index.php?page=order&id=<?=$i['id']?>" class="link1">View</a> <a href="index.php?page=order_manage&id=<?=$i['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="pagination">
<?php if ($pagination_page > 1): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page-1?>&order=<?=$order?>&order_by=<?=$order_by?>">Prev</a>
<?php endif; ?>
<span>Page <?=$pagination_page?> of <?=ceil($orders_total / $results_per_page) == 0 ? 1 : ceil($orders_total / $results_per_page)?></span>
<?php if ($pagination_page * $results_per_page < $orders_total): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page+1?>&order=<?=$order?>&order_by=<?=$order_by?>">Next</a>
<?php endif; ?>
</div>
<?=template_admin_footer()?>

437
admin/product.php Normal file
View File

@@ -0,0 +1,437 @@
<?php
defined('admin') or exit;
// Default input product values
$product = [
'name' => '',
'description' => '',
'price' => '',
'rrp' => '',
'quantity' => '',
'date_added' => date('Y-m-d\TH:i'),
'media' => [],
'categories' => [],
'options' => [],
'downloads' => [],
'weight' => '',
'url_slug' => '',
'status' => 1,
'productcode' => ''
];
// Get all the categories from the database
$stmt = $pdo->query('SELECT * FROM categories');
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Add product images to the database
function addProductImages($pdo, $product_id) {
// Get the total number of media
if (isset($_POST['media']) && is_array($_POST['media']) && count($_POST['media']) > 0) {
// Iterate media
$delete_list = [];
for ($i = 0; $i < count($_POST['media']); $i++) {
// If the media doesnt exist in the database
if (!intval($_POST['media_product_id'][$i])) {
// Insert new media
$stmt = $pdo->prepare('INSERT INTO products_media (product_id,media_id,position) VALUES (?,?,?)');
$stmt->execute([ $product_id, $_POST['media'][$i], $_POST['media_position'][$i] ]);
$delete_list[] = $pdo->lastInsertId();
} else {
// Update existing media
$stmt = $pdo->prepare('UPDATE products_media SET position = ? WHERE id = ?');
$stmt->execute([ $_POST['media_position'][$i], $_POST['media_product_id'][$i] ]);
$delete_list[] = $_POST['media_product_id'][$i];
}
}
// Delete media
$in = str_repeat('?,', count($delete_list) - 1) . '?';
$stmt = $pdo->prepare('DELETE FROM products_media WHERE product_id = ? AND id NOT IN (' . $in . ')');
$stmt->execute(array_merge([ $product_id ], $delete_list));
} else {
// No media exists, delete all
$stmt = $pdo->prepare('DELETE FROM products_media WHERE product_id = ?');
$stmt->execute([ $product_id ]);
}
}
// Add product categories to the database
function addProductCategories($pdo, $product_id) {
if (isset($_POST['categories']) && is_array($_POST['categories']) && count($_POST['categories']) > 0) {
$in = str_repeat('?,', count($_POST['categories']) - 1) . '?';
$stmt = $pdo->prepare('DELETE FROM products_categories WHERE product_id = ? AND category_id NOT IN (' . $in . ')');
$stmt->execute(array_merge([ $product_id ], $_POST['categories']));
foreach ($_POST['categories'] as $cat) {
$stmt = $pdo->prepare('INSERT IGNORE INTO products_categories (product_id,category_id) VALUES (?,?)');
$stmt->execute([ $product_id, $cat ]);
}
} else {
$stmt = $pdo->prepare('DELETE FROM products_categories WHERE product_id = ?');
$stmt->execute([ $product_id ]);
}
}
// Add product options to the database
function addProductOptions($pdo, $product_id) {
if (isset($_POST['option_title']) && is_array($_POST['option_title']) && count($_POST['option_title']) > 0) {
$delete_list = [];
for ($i = 0; $i < count($_POST['option_title']); $i++) {
$delete_list[] = $_POST['option_title'][$i] . '__' . $_POST['option_name'][$i];
$stmt = $pdo->prepare('INSERT INTO products_options (title,name,quantity,price,price_modifier,weight,weight_modifier,type,required,position,product_id) VALUES (?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE quantity = VALUES(quantity), price = VALUES(price), price_modifier = VALUES(price_modifier), weight = VALUES(weight), weight_modifier = VALUES(weight_modifier), type = VALUES(type), required = VALUES(required), position = VALUES(position)');
$stmt->execute([ $_POST['option_title'][$i], $_POST['option_name'][$i], empty($_POST['option_quantity'][$i]) ? -1 : $_POST['option_quantity'][$i], empty($_POST['option_price'][$i]) ? 0.00 : $_POST['option_price'][$i], $_POST['option_price_modifier'][$i], empty($_POST['option_weight'][$i]) ? 0.00 : $_POST['option_weight'][$i], $_POST['option_weight_modifier'][$i], $_POST['option_type'][$i], $_POST['option_required'][$i], $_POST['option_position'][$i], $product_id ]);
}
$in = str_repeat('?,', count($delete_list) - 1) . '?';
$stmt = $pdo->prepare('DELETE FROM products_options WHERE product_id = ? AND CONCAT(title, "__", name) NOT IN (' . $in . ')');
$stmt->execute(array_merge([ $product_id ], $delete_list));
} else {
$stmt = $pdo->prepare('DELETE FROM products_options WHERE product_id = ?');
$stmt->execute([ $product_id ]);
}
}
// Add product downloads to the database
function addProductDownloads($pdo, $product_id) {
if (isset($_POST['download_file_path']) && is_array($_POST['download_file_path']) && count($_POST['download_file_path']) > 0) {
$delete_list = [];
for ($i = 0; $i < count($_POST['download_file_path']); $i++) {
$delete_list[] = $_POST['download_file_path'][$i];
$stmt = $pdo->prepare('INSERT INTO products_downloads (product_id,file_path,position) VALUES (?,?,?) ON DUPLICATE KEY UPDATE position = VALUES(position)');
$stmt->execute([ $product_id, $_POST['download_file_path'][$i], $_POST['download_position'][$i] ]);
}
$in = str_repeat('?,', count($delete_list) - 1) . '?';
$stmt = $pdo->prepare('DELETE FROM products_downloads WHERE product_id = ? AND file_path NOT IN (' . $in . ')');
$stmt->execute(array_merge([ $product_id ], $delete_list));
} else {
$stmt = $pdo->prepare('DELETE FROM products_downloads WHERE product_id = ?');
$stmt->execute([ $product_id ]);
}
}
if (isset($_GET['id'])) {
// ID param exists, edit an existing product
$page = 'Edit';
if (isset($_POST['submit'])) {
//decode product_config to JSON
$product_config = $_POST['product_config'] ?? '';
$productcode = $_POST['productcode'] ?? '';
// Update the product
$stmt = $pdo->prepare('UPDATE products SET name = ?, description = ?, price = ?, rrp = ?, quantity = ?, date_added = ?, weight = ?, url_slug = ?, status = ?, product_config = ?, productcode = ? WHERE id = ?');
$stmt->execute([ $_POST['name'], $_POST['description'], empty($_POST['price']) ? 0.00 : $_POST['price'], empty($_POST['rrp']) ? 0.00 : $_POST['rrp'], $_POST['quantity'], date('Y-m-d H:i:s', strtotime($_POST['date'])), empty($_POST['weight']) ? 0.00 : $_POST['weight'], $_POST['url_slug'], $_POST['status'], $product_config, $productcode, $_GET['id'] ]);
addProductImages($pdo, $_GET['id']);
addProductCategories($pdo, $_GET['id']);
addProductOptions($pdo, $_GET['id']);
addProductDownloads($pdo, $_GET['id']);
// Clear session cart
if (isset($_SESSION['cart'])) {
unset($_SESSION['cart']);
}
header('Location: index.php?page=products&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Redirect and delete product
header('Location: index.php?page=products&delete=' . $_GET['id']);
exit;
}
// Get the product and its images from the database
$stmt = $pdo->prepare('SELECT * FROM products WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// get product media
$stmt = $pdo->prepare('SELECT m.*, pm.position, pm.id AS product_id FROM media m JOIN products_media pm ON pm.media_id = m.id JOIN products p ON p.id = pm.product_id WHERE p.id = ? ORDER BY pm.position');
$stmt->execute([ $_GET['id'] ]);
$product['media'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the product categories
$stmt = $pdo->prepare('SELECT c.name, c.id FROM products_categories pc JOIN categories c ON c.id = pc.category_id WHERE pc.product_id = ?');
$stmt->execute([ $_GET['id'] ]);
$product['categories'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the product options
$stmt = $pdo->prepare('SELECT title, type, GROUP_CONCAT(name) AS list FROM products_options WHERE product_id = ? GROUP BY title, type, position ORDER BY position');
$stmt->execute([ $_GET['id'] ]);
$product['options'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the product full options
$stmt = $pdo->prepare('SELECT * FROM products_options WHERE product_id = ? ORDER BY id');
$stmt->execute([ $_GET['id'] ]);
$product['options_full'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get the product downloads
$stmt = $pdo->prepare('SELECT * FROM products_downloads WHERE product_id = ? ORDER BY position');
$stmt->execute([ $_GET['id'] ]);
$product['downloads'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
// Create a new product
$page = 'Create';
if (isset($_POST['submit'])) {
$product_config = $_POST['product_config'] ?? '';
$productcode = $_POST['productcode'] ?? '';
$stmt = $pdo->prepare('INSERT INTO products (name,description,price,rrp,quantity,date_added,weight,url_slug,status, product_config, productcode) VALUES (?,?,?,?,?,?,?,?,?,?,?)');
$stmt->execute([ $_POST['name'], $_POST['description'], empty($_POST['price']) ? 0.00 : $_POST['price'], empty($_POST['rrp']) ? 0.00 : $_POST['rrp'], $_POST['quantity'], date('Y-m-d H:i:s', strtotime($_POST['date'])), empty($_POST['weight']) ? 0.00 : $_POST['weight'], $_POST['url_slug'], $_POST['status'], $product_config, $productcode ]);
$id = $pdo->lastInsertId();
addProductImages($pdo, $id);
addProductCategories($pdo, $id);
addProductOptions($pdo, $id);
addProductDownloads($pdo, $id);
// Clear session cart
if (isset($_SESSION['cart'])) {
unset($_SESSION['cart']);
}
header('Location: index.php?page=products&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Product', 'products', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Product</h2>
<a href="index.php?page=products" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this product?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="tabs">
<a href="#" class="active">General</a>
<a href="#">Media</a>
<a href="#">Configuration</a>
<a href="#">Options</a>
<a href="#">Downloads</a>
</div>
<!-- general tab -->
<div class="content-block tab-content active">
<div class="form responsive-width-100">
<label for="name"> Productcode</label>
<input id="name" type="text" name="productcode" placeholder="Productcode" value="<?=$product['productcode']?>">
<label for="name"><i class="required">*</i> Name</label>
<input id="name" type="text" name="name" placeholder="Name" value="<?=$product['name']?>" required>
<label for="description">Description (HTML)</label>
<textarea id="description" name="description" placeholder="Product Description..."><?=$product['description']?></textarea>
<label for="url_slug">URL Slug</label>
<input id="url_slug" type="text" name="url_slug" placeholder="your-product-name" value="<?=$product['url_slug']?>" title="If the rewrite URL setting is enabled, the URL slug will appear after the trailing slash as opposed to the product ID.">
<label for="price"><i class="required">*</i> Price</label>
<input id="price" type="number" name="price" placeholder="Price" min="0" step=".01" value="<?=$product['price']?>" required>
<label for="rrp">RRP</label>
<input id="rrp" type="number" name="rrp" placeholder="RRP" min="0" step=".01" value="<?=$product['rrp']?>">
<label for="quantity"><i class="required">*</i> Quantity</span></label>
<input id="quantity" type="number" name="quantity" placeholder="Quantity" min="-1" value="<?=$product['quantity']?>" title="-1 = unlimited" required>
<label for="category">Categories</label>
<div class="multiselect" data-name="categories[]">
<?php foreach ($product['categories'] as $cat): ?>
<span class="item" data-value="<?=$cat['id']?>">
<i class="remove">&times;</i><?=$cat['name']?>
<input type="hidden" name="categories[]" value="<?=$cat['id']?>">
</span>
<?php endforeach; ?>
<input type="text" class="search" id="category" placeholder="Categories">
<div class="list">
<?php foreach ($categories as $cat): ?>
<span data-value="<?=$cat['id']?>"><?=$cat['name']?></span>
<?php endforeach; ?>
</div>
</div>
<label for="weight">Weight (lbs)</span></label>
<input id="weight" type="number" name="weight" placeholder="Weight (lbs)" min="0" value="<?=$product['weight']?>">
<label for="status">Status</label>
<select id="status" name="status">
<option value="1"<?=$product['status']==1?' selected':''?>>Enabled</option>
<option value="0"<?=$product['status']==0?' selected':''?>>Disabled</option>
</select>
<label for="date"><i class="required">*</i> Date Added</label>
<input id="date" type="datetime-local" name="date" placeholder="Date" value="<?=date('Y-m-d\TH:i', strtotime($product['date_added']))?>" required>
</div>
</div>
<!-- product media tab -->
<div class="content-block tab-content">
<div class="pad-3 product-media-tab responsive-width-100">
<h3 class="title1 mar-bot-5">Images</h3>
<div class="product-media-container">
<?php if (isset($product['media'])): ?>
<?php foreach ($product['media'] as $i => $media): ?>
<div class="product-media">
<span class="media-index responsive-hidden"><?=$i+1?></span>
<a class="media-img" href="../<?=$media['full_path']?>" target="_blank">
<img src="../<?=$media['full_path']?>">
</a>
<div class="media-text">
<h3 class="responsive-hidden"><?=$media['title']?></h3>
<p class="responsive-hidden"><?=$media['caption']?></p>
</div>
<div class="media-position">
<i class="fas fa-times media-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
<input type="hidden" class="input-media-id" name="media[]" value="<?=$media['id']?>">
<input type="hidden" class="input-media-product-id" name="media_product_id[]" value="<?=$media['product_id']?>">
<input type="hidden" class="input-media-position" name="media_position[]" value="<?=$media['position']?>">
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (empty($product['media'])): ?>
<p class="no-images-msg">There are no images.</p>
<?php endif; ?>
</div>
<a href="#" class="btn open-media-library-modal mar-bot-2 mar-top-4">Add Media</a>
</div>
</div>
<!-- product media configuration -->
<div class="content-block tab-content">
<div class="pad-3 product-media-tab responsive-width-100">
<h3 class="title1 mar-bot-5">Available Images</h3>
<div class="product-media-container">
<?php if (isset($product['media'])): ?>
<div class="product-media">
<?php foreach ($product['media'] as $i => $media): ?>
<span class="media-index"><?=$media['id']?></span>
<a class="media-img" href="../<?=$media['full_path']?>" target="_blank">
<img src="../<?=$media['full_path']?>">
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (empty($product['media'])): ?>
<p class="no-images-msg">There are no images.</p>
<?php endif; ?>
</div>
<h3 class="title1 mar-bot-5">Available Options</h3>
<div class="">
<?php if (isset($product['options'])): ?>
<?php foreach ($product['options'] as $i => $option): ?>
<div style="display: flex;">
<?php foreach ($product['options_full'] as $option_full): ?>
<?php if ($option['title'] != $option_full['title']) continue; ?>
<p><?=$option_full['id']?> - <?=$option_full['name']?> - <?=$option_full['title']?></p>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (empty($product['options'])): ?>
<p class="no-options-msg">There are no options.</p>
<?php endif; ?>
</div>
<h3 class="title1 mar-bot-5">Configuration JSON Profile</h3>
<textarea name="product_config" id="product_config" style="width:100%;min-height: 50vh;"><?=$product['product_config']?></textarea>
</div>
</div>
<!-- options tab -->
<div class="content-block tab-content">
<div class="pad-3 product-options-tab responsive-width-100">
<h3 class="title1 mar-bot-5">Options (be aware of changing optionIDs)</h3>
<div class="product-options-container">
<?php if (isset($product['options'])): ?>
<?php foreach ($product['options'] as $i => $option): ?>
<div class="product-option">
<span class="option-index responsive-hidden"><?=$i+1?></span>
<div class="option-text">
<h3><?=$option['title']?> (<?=$option['type']?>)</h3>
<p><?=str_replace(',', ', ', $option['list'])?></p>
</div>
<div class="option-position">
<i class="fas fa-pen option-edit"></i>
<i class="fas fa-times option-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
<?php foreach ($product['options_full'] as $option_full): ?>
<?php if ($option['title'] != $option_full['title']) continue; ?>
<div class="input-option-value">
<input type="hidden" class="input-option-title" name="option_title[]" value="<?=$option_full['title']?>">
<input type="hidden" class="input-option-name" name="option_name[]" value="<?=$option_full['name']?>">
<input type="hidden" class="input-option-quantity" name="option_quantity[]" value="<?=$option_full['quantity']?>">
<input type="hidden" class="input-option-price" name="option_price[]" value="<?=$option_full['price']?>">
<input type="hidden" class="input-option-price-modifier" name="option_price_modifier[]" value="<?=$option_full['price_modifier']?>">
<input type="hidden" class="input-option-weight" name="option_weight[]" value="<?=$option_full['weight']?>">
<input type="hidden" class="input-option-weight-modifier" name="option_weight_modifier[]" value="<?=$option_full['weight_modifier']?>">
<input type="hidden" class="input-option-type" name="option_type[]" value="<?=$option_full['type']?>">
<input type="hidden" class="input-option-required" name="option_required[]" value="<?=$option_full['required']?>">
<input type="hidden" class="input-option-position" name="option_position[]" value="<?=$option_full['position']?>">
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (empty($product['options'])): ?>
<p class="no-options-msg">There are no options.</p>
<?php endif; ?>
</div>
<a href="#" class="btn open-options-modal mar-bot-2 mar-top-4">Add Option</a>
</div>
</div>
<!-- digital downloads tab -->
<div class="content-block tab-content">
<div class="pad-3 product-options-tab responsive-width-100">
<h3 class="title1 mar-bot-5">Digital Downloads</h3>
<div class="product-downloads-container">
<?php if (isset($product['downloads'])): ?>
<?php foreach ($product['downloads'] as $i => $download): ?>
<?php if (!file_exists('../' . $download['file_path'])) continue; ?>
<div class="product-download">
<span class="download-index responsive-hidden"><?=$i+1?></span>
<div class="download-text">
<h3><?=$download['file_path']?></h3>
<p><?=mime_content_type('../' . $download['file_path'])?>, <?=format_bytes(filesize('../' . $download['file_path']))?></p>
</div>
<div class="download-position">
<i class="fas fa-times download-delete"></i>
<i class="fas fa-arrow-up move-up"></i>
<i class="fas fa-arrow-down move-down"></i>
</div>
<div class="input-option-value">
<input type="hidden" class="input-download-file-path" name="download_file_path[]" value="<?=$download['file_path']?>">
<input type="hidden" class="input-download-position" name="download_position[]" value="<?=$download['position']?>">
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (empty($product['downloads'])): ?>
<p class="no-downloads-msg">There are no digital downloads.</p>
<?php endif; ?>
</div>
<a href="#" class="btn open-downloads-modal mar-bot-2 mar-top-4">Add Digital Download</a>
</div>
</div>
</form>
<?=template_admin_footer('initProduct()')?>

177
admin/products.php Normal file
View File

@@ -0,0 +1,177 @@
<?php
defined('admin') or exit;
// Retrieve the GET request parameters (if specified)
$pagination_page = isset($_GET['pagination_page']) ? $_GET['pagination_page'] : 1;
$search = isset($_GET['search']) ? $_GET['search'] : '';
// Filters parameters
$status = isset($_GET['status']) ? $_GET['status'] : '';
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : '';
// Order by column
$order = isset($_GET['order']) && $_GET['order'] == 'DESC' ? 'DESC' : 'ASC';
// Add/remove columns to the whitelist array
$order_by_whitelist = ['id','name','price','quantity','date_added','status'];
$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], $order_by_whitelist) ? $_GET['order_by'] : 'id';
// Number of results per pagination page
$results_per_page = 20;
// Declare query param variables
$param1 = ($pagination_page - 1) * $results_per_page;
$param2 = $results_per_page;
$param3 = '%' . $search . '%';
// SQL where clause
$where = '';
$where .= $search ? 'WHERE (p.name LIKE :search) ' : '';
// Add filters
if ($status == 'one') {
$where .= $where ? 'AND p.status = 1 ' : 'WHERE p.status = 1 ';
}
if ($status == 'zero') {
$where .= $where ? 'AND p.status = 0 ' : 'WHERE p.status = 0 ';
}
if ($quantity == 'zero') {
$where .= $where ? 'AND p.quantity = 0 ' : 'WHERE p.quantity = 0 ';
}
// Retrieve the total number of products
$stmt = $pdo->prepare('SELECT COUNT(*) AS total FROM products p ' . $where);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
$products_total = $stmt->fetchColumn();
// SQL query to get all products from the "products" table
$stmt = $pdo->prepare('SELECT p.*, GROUP_CONCAT(m2.full_path) AS imgs FROM products p LEFT JOIN (SELECT pm.id, pm.product_id, m.full_path FROM products_media pm JOIN media m ON m.id = pm.media_id GROUP BY pm.id, pm.product_id, m.full_path) m2 ON m2.product_id = p.id ' . $where . ' GROUP BY p.id, p.name, p.description, p.price, p.rrp, p.quantity, p.date_added, p.weight, p.url_slug, p.status ORDER BY ' . $order_by . ' ' . $order . ' LIMIT :start_results,:num_results');
// Bind params
$stmt->bindParam('start_results', $param1, PDO::PARAM_INT);
$stmt->bindParam('num_results', $param2, PDO::PARAM_INT);
if ($search) $stmt->bindParam('search', $param3, PDO::PARAM_STR);
$stmt->execute();
// Retrieve query results
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Delete product
if (isset($_GET['delete'])) {
// Delete the product
$stmt = $pdo->prepare('DELETE p, pm, po, pc FROM products p LEFT JOIN products_media pm ON pm.product_id = p.id LEFT JOIN products_options po ON po.product_id = p.id LEFT JOIN products_categories pc ON pc.product_id = p.id WHERE p.id = ?');
$stmt->execute([ $_GET['delete'] ]);
// Clear session cart
if (isset($_SESSION['cart'])) {
unset($_SESSION['cart']);
}
header('Location: index.php?page=products&success_msg=3');
exit;
}
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Product created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Product updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Product deleted successfully!';
}
}
// Determine the URL
$url = 'index.php?page=products&search=' . $search . '&status=' . $status . '&quantity=' . $quantity;
?>
<?=template_admin_header('Products', 'products', 'view')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-box-open"></i>
<div class="txt">
<h2>Products</h2>
<p>View, manage, and search products.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=product" class="btn">Create Product</a>
<form action="" method="get">
<input type="hidden" name="page" value="products">
<div class="filters">
<a href="#"><i class="fas fa-sliders-h"></i> Filters</a>
<div class="list">
<label><input type="checkbox" name="status" value="one"<?=$status=='one'?' checked':''?>>Enabled</label>
<label><input type="checkbox" name="status" value="zero"<?=$status=='zero'?' checked':''?>>Disabled</label>
<label><input type="checkbox" name="quantity" value="zero"<?=$quantity=='zero'?' checked':''?>>No Stock</label>
<button type="submit">Apply</button>
</div>
</div>
<div class="search">
<label for="search">
<input id="search" type="text" name="search" placeholder="Search product name..." value="<?=htmlspecialchars($search, ENT_QUOTES)?>" class="responsive-width-100">
<i class="fas fa-search"></i>
</label>
</div>
</form>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=id'?>">#<?php if ($order_by=='id'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td>Productcode</td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=name'?>">Name<?php if ($order_by=='name'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=price'?>">Price<?php if ($order_by=='price'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=quantity'?>">Quantity<?php if ($order_by=='quantity'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden">Images</td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=date_added'?>">Date Added<?php if ($order_by=='date_added'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td class="responsive-hidden"><a href="<?=$url . '&order=' . ($order=='ASC'?'DESC':'ASC') . '&order_by=status'?>">Status<?php if ($order_by=='status'): ?><i class="fas fa-level-<?=str_replace(['ASC', 'DESC'], ['up','down'], $order)?>-alt fa-xs"></i><?php endif; ?></a></td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($products)): ?>
<tr>
<td colspan="8" style="text-align:center;">There are no products</td>
</tr>
<?php else: ?>
<?php foreach ($products as $product): ?>
<tr>
<td class="responsive-hidden"><?=$product['id']?></td>
<td><?=$product['productcode']?></td>
<td><?=$product['name']?></td>
<?php if ($product['rrp'] == 0.00): ?>
<td><?=currency_code?><?=number_format($product['price'], 2)?></td>
<?php else: ?>
<td><span class="rrp"><?=currency_code?><?=number_format($product['price'], 2)?></span> <s><?=currency_code . number_format($product['rrp'], 2)?></s></td>
<?php endif; ?>
<td><?=$product['quantity']==-1?'--':number_format($product['quantity'])?></td>
<td class="responsive-hidden img">
<?php foreach (array_reverse(explode(',',$product['imgs'])) as $img): ?>
<?php if ($img): ?>
<img src="../<?=$img?>" width="32" height="32" alt="<?=$img?>">
<?php endif; ?>
<?php endforeach; ?>
</td>
<td class="responsive-hidden"><?=date('F j, Y', strtotime($product['date_added']))?></td>
<td class="responsive-hidden"><?=$product['status'] ? 'Enabled' : 'Disabled'?></td>
<td><a href="index.php?page=product&id=<?=$product['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="pagination">
<?php if ($pagination_page > 1): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page-1?>&order=<?=$order?>&order_by=<?=$order_by?>">Prev</a>
<?php endif; ?>
<span>Page <?=$pagination_page?> of <?=ceil($products_total / $results_per_page) == 0 ? 1 : ceil($products_total / $results_per_page)?></span>
<?php if ($pagination_page * $results_per_page < $products_total): ?>
<a href="<?=$url?>&pagination_page=<?=$pagination_page+1?>&order=<?=$order?>&order_by=<?=$order_by?>">Next</a>
<?php endif; ?>
</div>
<?=template_admin_footer()?>

109
admin/settings.php Normal file
View File

@@ -0,0 +1,109 @@
<?php
defined('admin') or exit;
// Configuration file
$file = '../config.php';
// Open the configuration file for reading
$contents = file_get_contents($file);
// Format key function
function format_key($key) {
$key = str_replace(
['_', 'url', 'db ', ' pass', ' user', 'ipn', 'paypal'],
[' ', 'URL', 'Database ', ' Password', ' Username', 'IPN', 'PayPal'],
strtolower($key)
);
return ucwords($key);
}
// Format HTML output function
function format_var_html($key, $value) {
$html = '';
$type = 'text';
$value = htmlspecialchars(trim($value, '\''), ENT_QUOTES);
$type = strpos($key, 'pass') !== false ? 'password' : $type;
$type = in_array(strtolower($value), ['true', 'false']) ? 'checkbox' : $type;
$checked = strtolower($value) == 'true' ? ' checked' : '';
$html .= '<label for="' . $key . '">' . format_key($key) . '</label>';
if ($type == 'checkbox') {
$html .= '<input type="hidden" name="' . $key . '" value="false">';
}
$html .= '<input type="' . $type . '" name="' . $key . '" id="' . $key . '" value="' . $value . '" placeholder="' . format_key($key) . '"' . $checked . '>';
return $html;
}
// Format tabs
function format_tabs($contents) {
$rows = explode("\n", $contents);
echo '<div class="tabs">';
echo '<a href="#" class="active">General</a>';
for ($i = 0; $i < count($rows); $i++) {
preg_match('/\/\*(.*?)\*\//', $rows[$i], $match);
if ($match) {
echo '<a href="#">' . $match[1] . '</a>';
}
}
echo '</div>';
}
// Format form
function format_form($contents) {
$rows = explode("\n", $contents);
echo '<div class="tab-content active">';
for ($i = 0; $i < count($rows); $i++) {
preg_match('/\/\*(.*?)\*\//', $rows[$i], $match);
if ($match) {
echo '</div><div class="tab-content">';
}
preg_match('/define\(\'(.*?)\', ?(.*?)\)/', $rows[$i], $match);
if ($match) {
echo format_var_html($match[1], $match[2]);
}
}
echo '</div>';
}
if (!empty($_POST)) {
// Update the configuration file with the new keys and values
foreach ($_POST as $k => $v) {
$v = in_array(strtolower($v), ['true', 'false']) ? strtolower($v) : '\'' . $v . '\'';
$contents = preg_replace('/define\(\'' . $k . '\'\, ?(.*?)\)/s', 'define(\'' . $k . '\',' . $v . ')', $contents);
}
file_put_contents('../config.php', $contents);
header('Location: index.php?page=settings&success_msg=1');
exit;
}
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Settings updated successfully!';
}
}
?>
<?=template_admin_header('Settings', 'settings')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Settings</h2>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<?=format_tabs($contents)?>
<div class="content-block">
<div class="form responsive-width-100">
<?=format_form($contents)?>
</div>
</div>
</form>
<script>
document.querySelectorAll("input[type='checkbox']").forEach(checkbox => {
checkbox.onclick = () => checkbox.value = checkbox.checked ? 'true' : 'false';
});
</script>
<?=template_admin_footer()?>

83
admin/shipping.php Normal file
View File

@@ -0,0 +1,83 @@
<?php
defined('admin') or exit;
// SQL query to get all shipping methods from the "shipping" table
$stmt = $pdo->prepare('SELECT * FROM shipping');
$stmt->execute();
$shipping = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Shipping method created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Shipping method updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Shipping method deleted successfully!';
}
}
?>
<?=template_admin_header('Shipping', 'shipping')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-truck-fast"></i>
<div class="txt">
<h2>Shipping</h2>
<p>View, create, and edit shipping methods.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=shipping_process" class="btn">Create Shipping Method</a>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td>#</td>
<td>Name</td>
<td>Type</td>
<td class="responsive-hidden">Countries</td>
<td class="responsive-hidden">Price Range</td>
<td class="responsive-hidden">Weight Range</td>
<td>Total Shipping Price</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($shipping)): ?>
<tr>
<td colspan="8" style="text-align:center;">There are no shipping methods</td>
</tr>
<?php else: ?>
<?php foreach ($shipping as $s): ?>
<tr>
<td><?=$s['id']?></td>
<td><?=$s['name']?></td>
<td><?=$s['type']?></td>
<td class="responsive-hidden" style="max-width:300px"><?=$s['countries'] ? str_replace(',', ', ', $s['countries']) : 'all'?></td>
<td class="responsive-hidden"><?=currency_code?><?=number_format($s['price_from'], 2)?> - <?=currency_code?><?=number_format($s['price_to'], 2)?></td>
<td class="responsive-hidden"><?=number_format($s['weight_from'], 2)?> lbs - <?=number_format($s['weight_to'], 2)?> lbs</td>
<td><?=currency_code?><?=number_format($s['price'], 2)?></td>
<td><a href="index.php?page=shipping_process&id=<?=$s['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?=template_admin_footer()?>

116
admin/shipping_process.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
defined('admin') or exit;
// Default input shipping values
$shipping = [
'name' => '',
'price_from' => '',
'price_to' => '',
'weight_from' => '',
'weight_to' => '',
'price' => '',
'type' => 'Single Product',
'countries' => ''
];
$types = ['Single Product', 'Entire Order'];
if (isset($_GET['id'])) {
// ID param exists, edit an existing shipping method
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the shipping method
$countries_list = isset($_POST['countries']) ? implode(',', $_POST['countries']) : '';
$stmt = $pdo->prepare('UPDATE shipping SET name = ?, price_from = ?, price_to = ?, weight_from = ?, weight_to = ?, price = ?, type = ?, countries = ? WHERE id = ?');
$stmt->execute([ $_POST['name'], $_POST['price_from'], $_POST['price_to'], $_POST['weight_from'], $_POST['weight_to'], $_POST['price'], $_POST['type'], $countries_list, $_GET['id'] ]);
header('Location: index.php?page=shipping&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the shipping method
$stmt = $pdo->prepare('DELETE FROM shipping WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
header('Location: index.php?page=shipping&success_msg=3');
exit;
}
// Get the shipping method from the database
$stmt = $pdo->prepare('SELECT * FROM shipping WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$shipping = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
// Create a new shipping method
$page = 'Create';
if (isset($_POST['submit'])) {
$countries_list = isset($_POST['countries']) ? implode(',', $_POST['countries']) : '';
$stmt = $pdo->prepare('INSERT INTO shipping (name, price_from, price_to, weight_from, weight_to, price, type, countries) VALUES (?,?,?,?,?,?,?,?)');
$stmt->execute([ $_POST['name'], $_POST['price_from'], $_POST['price_to'], $_POST['weight_from'], $_POST['weight_to'], $_POST['price'], $_POST['type'], $countries_list ]);
header('Location: index.php?page=shipping&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Shipping Method', 'shipping', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Shipping Method</h2>
<a href="index.php?page=shipping" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this shipping method?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="content-block">
<div class="form responsive-width-100">
<label for="name"><i class="required">*</i> Name</label>
<input type="text" name="name" placeholder="Name" value="<?=$shipping['name']?>" required>
<label for="type"><i class="required">*</i> Type</label>
<select name="type" id="type" required>
<?php foreach ($types as $type): ?>
<option value="<?=$type?>"<?=$shipping['type'] == $type ? ' selected' : ''?>><?=$type?></option>
<?php endforeach; ?>
</select>
<label for="countries">Countries</label>
<div class="multiselect" data-name="countries[]">
<?php foreach (explode(',', $shipping['countries']) as $c): ?>
<?php if (empty($c)) continue; ?>
<span class="item" data-value="<?=$c?>">
<i class="remove">&times;</i><?=$c?>
<input type="hidden" name="countries[]" value="<?=$c?>">
</span>
<?php endforeach; ?>
<input type="text" class="search" id="countries" placeholder="Countries">
<div class="list">
<?php foreach (get_countries() as $country): ?>
<span data-value="<?=$country?>"><?=$country?></span>
<?php endforeach; ?>
</div>
</div>
<label for="price"><i class="required">*</i> Product Price Range</label>
<div style="display:flex;margin:0;">
<input type="number" name="price_from" placeholder="From" min="0" step=".01" value="<?=$shipping['price_from']?>" required>
<span style="padding-top:15px">&nbsp;&nbsp;&nbsp;&mdash;&nbsp;&nbsp;&nbsp;</span>
<input type="number" name="price_to" placeholder="To" min="0" step=".01" value="<?=$shipping['price_to']?>" required>
</div>
<label for="price"><i class="required">*</i> Product Weight Range (lbs)</label>
<div style="display:flex;margin:0;">
<input type="number" name="weight_from" placeholder="From" min="0" step=".01" value="<?=$shipping['weight_from']?>" required>
<span style="padding-top:15px">&nbsp;&nbsp;&nbsp;&mdash;&nbsp;&nbsp;&nbsp;</span>
<input type="number" name="weight_to" placeholder="To" min="0" step=".01" value="<?=$shipping['weight_to']?>" required>
</div>
<label for="name"><i class="required">*</i> Total Shipping Price</label>
<input type="number" name="price" placeholder="3.99" min="0" step=".01" value="<?=$shipping['price']?>" required>
</div>
</div>
</form>
<?=template_admin_footer()?>

75
admin/tax.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
defined('admin') or exit;
// Default input tax values
$tax = [
'country' => '',
'rate' => 0.00
];
if (isset($_GET['id'])) {
// ID param exists, edit an existing tax
$page = 'Edit';
if (isset($_POST['submit'])) {
// Update the tax
$categories_list = isset($_POST['categories']) ? implode(',', $_POST['categories']) : '';
$products_list = isset($_POST['products']) ? implode(',', $_POST['products']) : '';
$stmt = $pdo->prepare('UPDATE taxes SET country = ?, rate = ? WHERE id = ?');
$stmt->execute([ $_POST['country'], $_POST['rate'], $_GET['id'] ]);
header('Location: index.php?page=taxes&success_msg=2');
exit;
}
if (isset($_POST['delete'])) {
// Delete the tax
$stmt = $pdo->prepare('DELETE FROM taxes WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
header('Location: index.php?page=taxes&success_msg=3');
exit;
}
// Get the tax from the database
$stmt = $pdo->prepare('SELECT * FROM taxes WHERE id = ?');
$stmt->execute([ $_GET['id'] ]);
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
// Create a new tax
$page = 'Create';
if (isset($_POST['submit'])) {
$stmt = $pdo->prepare('INSERT INTO taxes (country,rate) VALUES (?,?)');
$stmt->execute([ $_POST['country'], $_POST['rate'] ]);
header('Location: index.php?page=taxes&success_msg=1');
exit;
}
}
?>
<?=template_admin_header($page . ' Tax', 'taxes', 'manage')?>
<form action="" method="post">
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100"><?=$page?> Tax</h2>
<a href="index.php?page=taxes" class="btn alt mar-right-2">Cancel</a>
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="Delete" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this tax?')">
<?php endif; ?>
<input type="submit" name="submit" value="Save" class="btn">
</div>
<div class="content-block">
<div class="form responsive-width-100">
<label for="country"><i class="required">*</i> Country</label>
<select name="country" required>
<?php foreach (get_countries() as $country): ?>
<option value="<?=$country?>"<?=$country==$tax['country']?' selected':''?>><?=$country?></option>
<?php endforeach; ?>
</select>
<label for="rate"><i class="required">*</i> Rate</label>
<input id="rate" type="number" name="rate" step=".01" placeholder="Rate" value="<?=$tax['rate']?>" required>
</div>
</div>
</form>
<?=template_admin_footer()?>

75
admin/taxes.php Normal file
View File

@@ -0,0 +1,75 @@
<?php
defined('admin') or exit;
// SQL query to get all taxes from the "taxes" table
$stmt = $pdo->prepare('SELECT * FROM taxes ORDER BY country ASC');
$stmt->execute();
$taxes = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Handle success messages
if (isset($_GET['success_msg'])) {
if ($_GET['success_msg'] == 1) {
$success_msg = 'Tax created successfully!';
}
if ($_GET['success_msg'] == 2) {
$success_msg = 'Tax updated successfully!';
}
if ($_GET['success_msg'] == 3) {
$success_msg = 'Tax deleted successfully!';
}
}
?>
<?=template_admin_header('Taxes', 'taxes')?>
<div class="content-title">
<div class="title">
<i class="fa-solid fa-percent"></i>
<div class="txt">
<h2>Taxes</h2>
<p>View, create, and edit taxes.</p>
</div>
</div>
</div>
<?php if (isset($success_msg)): ?>
<div class="msg success">
<i class="fas fa-check-circle"></i>
<p><?=$success_msg?></p>
<i class="fas fa-times"></i>
</div>
<?php endif; ?>
<div class="content-header responsive-flex-column pad-top-5">
<a href="index.php?page=tax" class="btn">Create Tax</a>
</div>
<div class="content-block">
<div class="table">
<table>
<thead>
<tr>
<td class="responsive-hidden">#</td>
<td>Country</td>
<td>Tax Rate</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<?php if (empty($taxes)): ?>
<tr>
<td colspan="4" style="text-align:center;">There are no taxes</td>
</tr>
<?php else: ?>
<?php foreach ($taxes as $tax): ?>
<tr>
<td class="responsive-hidden"><?=$tax['id']?></td>
<td><?=$tax['country']?></td>
<td><?=$tax['rate']?>%</td>
<td><a href="index.php?page=tax&id=<?=$tax['id']?>" class="link1">Edit</a></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?=template_admin_footer()?>